From 2766a7f9f35a973cf4365e4f37de018bf27784c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=81=D0=B5=D0=BB=D1=91=D0=B2=20=D0=92=D0=BB?= =?UTF-8?q?=D0=B0=D0=B4=D0=B8=D1=81=D0=BB=D0=B0=D0=B2?= Date: Mon, 6 May 2019 16:53:18 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B2=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6=D0=BD=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D1=8C=20=D0=B1=D1=83=D0=BD=D0=BA=D0=B5=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=BA=D0=B8,=20=D1=83=D0=BF=D1=80=D0=BE=D1=89=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=20=D1=81=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constraints/conversion_2.mzn | 236 +++++---- src/inport/Bunker.java | 15 +- src/inport/ConversionUtil.java | 497 ++++++++++++------ src/inport/LoadingTemplate.java | 59 ++- src/inport/Main.java | 6 +- src/inport/Operation.java | 12 +- src/inport/Storage.java | 2 +- src/inport/StorageState.java | 15 +- src/inport/TaskCase.java | 74 ++- tests/with_typing/Bunkers.tipp | 94 ++++ tests/with_typing/v2.tipp | 2 +- ...\200\320\260\321\206\320\270\320\271.docx" | Bin 49894 -> 50469 bytes 12 files changed, 681 insertions(+), 331 deletions(-) create mode 100644 tests/with_typing/Bunkers.tipp diff --git a/src/constraints/conversion_2.mzn b/src/constraints/conversion_2.mzn index 90bb882..2ef635f 100644 --- a/src/constraints/conversion_2.mzn +++ b/src/constraints/conversion_2.mzn @@ -5,14 +5,19 @@ int : n_operations; int : n_locations; int : n_moving_obj; -array [1..n_operations, 0..(n_intervals + 1)] of var bool : op_status; -array [1..n_operations, 0..(n_intervals + 1)] of var bool : op_start; +array [0..n_operations, 0..(n_intervals + 1)] of var bool : op_status; +array [0..n_operations, 0..(n_intervals + 1)] of var bool : op_start; -array [1..n_moving_obj, 1..n_operations] of bool : moving_op_of_obj; +constraint forall (t in 0..(n_intervals + 1)) (op_status[0, t] = false); % Фиктивная операция. +constraint forall (t in 0..(n_intervals + 1)) ( op_start[0, t] = false); % Фиктивная операция. + +array [0..n_moving_obj, 0..n_operations] of bool : moving_op_of_obj; % Определение current_moving_operation. % Текущая операция операция перемещения, в которой задействован данный объект. - array [1..n_moving_obj, 0..(n_intervals + 1)] of var 0..n_operations : current_moving_operation; + array [0..n_moving_obj, 0..(n_intervals + 1)] of var 0..n_operations : current_moving_operation; + + constraint forall (t in 0..(n_intervals + 1)) (current_moving_operation[0, t] = 0); % Фиктивный объект. % Крайние значения. constraint forall (obj in 1..n_moving_obj, t in {0, n_intervals + 1}) ( @@ -32,59 +37,62 @@ array [1..n_moving_obj, 1..n_operations] of bool : moving_op_of_obj; (current_moving_operation[obj, t] != 0) -> (op_status[current_moving_operation[obj, t], t]) ); -% Ограничение на количество пришвартованных кораблей. - % Счётчик объектов в локации (только в чётных - в состоянии пришвартованности). - array [1..n_locations, 0..(n_intervals + 1)] of var int : obj_in_loc_counter; - - array [1..n_locations] of 1..n_locations : twin_location = [i - 1 + (i mod 2) * 2 | i in 1..n_locations]; - - % Операции грузообработки, проводимые без пришвартовки на этой локации. - array [1..n_locations] of set of 1..n_operations : related_unmoored_cargo_op; - - % Определение obj_in_loc_counter. - constraint forall (loc in 1..n_locations, t in 0..n_intervals where (loc mod 2) = 0) ( - obj_in_loc_counter[loc, t] = (sum (obj in 1..n_moving_obj) (m_obj_loc[obj, t + 1] = loc)) % Швартовка и нахождение у причала. - + (sum (op in related_unmoored_cargo_op[twin_location[loc]]) (op_status[op, t])) % Погрузка без швартовки. - % + (sum (op in 1..n_operations where ((not is_moving_operation[op]) /\ (main_obj_start_loc[op] = twin_location[loc]))) % Погрузка без швартовки. - % (op_status[op, t]) - % ) % TODO узнать, почему медленнее варианта выше. - + (sum (op in 1..n_operations where (is_mooring_op[op] /\ (main_obj_start_loc[op] = loc))) % Отшвартовка. - (op_status[op, t]) - ) +% Определение current_moored_obj. + % Объект, пришвартованный к данной локации. + array [0..n_locations, 0..(n_intervals + 1)] of var 0..n_moving_obj : current_moored_obj; + + constraint forall (t in 0..(n_intervals + 1)) (current_moored_obj[0, t] = 0); % Фиктивная локация. + + % Фиксирование неиспользуемых значений. + constraint forall (loc in 1..n_locations, t in 0..(n_intervals + 1) where loc mod 2 = 1) ( + current_moored_obj[loc, t] = 0 ); - % Само ограничение. - constraint forall (loc in 1..n_locations, t in 0..(n_intervals + 1) where (loc mod 2) = 0) ( - obj_in_loc_counter[loc, t] <= 1 + % Крайние значения. + constraint forall (loc in 1..n_locations, t in {0, n_intervals + 1}) ( + current_moored_obj[loc, t] = 0 ); -% Определение is_m_obj_in_movement_before_start. - % Находится ли объект в движении перед началом интервала. - array [1..n_moving_obj, 0..(n_intervals + 1)] of var bool : is_m_obj_in_movement_before_start; + % Операции с условной швартовкой. + int : op_with_nominally_mooring_max_size; + array [0..n_locations, 0..n_moving_obj] of 0..op_with_nominally_mooring_max_size : op_with_nominally_mooring_sizes; + array [0..n_locations, 0..n_moving_obj, 1..op_with_nominally_mooring_max_size] of 1..n_operations : op_with_nominally_mooring; + + % current_moored_obj соответствуе какому-либо реально пришвартованному объекту. + constraint forall (loc in 1..n_locations, t in 1..n_intervals, obj = current_moored_obj[loc, t] + where loc mod 2 = 0) ( + (obj != 0) -> ((m_obj_loc[obj, t] = loc) \/ + (exists (op in [op_with_nominally_mooring[loc, obj, id] | id in 1..op_with_nominally_mooring_sizes[loc, obj]]) + (op_status[op, t])) + ) + ); - % В начале никто не движется. - constraint forall (obj in 1..n_moving_obj) (not is_m_obj_in_movement_before_start[obj, 0]); - % Перед первым интервалом все тоже стоят на месте. - constraint forall (obj in 1..n_moving_obj) (not is_m_obj_in_movement_before_start[obj, 1]); + % Если объект пришвартован или швартуется, то current_moored_obj об это знает. + constraint forall (obj in 1..n_moving_obj, t in 1..n_intervals, loc = m_obj_loc[obj, t]) ( + (loc mod 2 = 0) -> (current_moored_obj[loc, t] = obj) + ); - % Если в предыдущем интервале объект не движется - то в начале этого он не движется, иначе он движется если предыдущая операция не закончилась. - constraint forall (obj in 1..n_moving_obj, t in 1..(n_intervals + 1)) ( - (current_moving_operation[obj, t - 1] = 0) - -> is_m_obj_in_movement_before_start[obj, t] = false - ); - constraint forall (obj in 1..n_moving_obj, t in 1..(n_intervals + 1)) ( - (current_moving_operation[obj, t - 1] != 0) - -> is_m_obj_in_movement_before_start[obj, t] = (current_moving_operation[obj, t - 1] == current_moving_operation[obj, t]) - ); + % Если выполняется операция, где судно условно пришвартовано, то current_moored_obj знает об этом. + constraint forall (loc in 1..n_locations, + obj in 1..n_moving_obj, + op in [op_with_nominally_mooring[loc, obj, id] | id in 1..op_with_nominally_mooring_sizes[loc, obj]], + t in 1..n_intervals) ( + op_status[op, t] -> (current_moored_obj[loc, t] = obj) + ); -array [1..n_operations] of 1..n_locations : operations_destination; % Локация в которой окажется объект после завершения операции. +array [1..n_locations] of 0..n_locations : twin_location = [i - 1 + (i mod 2) * 2 | i in 1..n_locations]; +% TODO нумерация с нуля + +array [0..n_operations] of 0..n_locations : operations_destination; % Локация в которой окажется объект после завершения операции. % Определение m_obj_loc. % Местоположение объекта или пункт назначения (если объект движется) перед началом определённого интервала. - array [1..n_moving_obj, 0..(n_intervals + 1)] of var 1..n_locations : m_obj_loc; + array [0..n_moving_obj, 0..(n_intervals + 1)] of var 0..n_locations : m_obj_loc; + + constraint forall (t in 0..(n_intervals + 1)) (m_obj_loc[0, t] = 0); % Фиктивный объект. % Главный объект (субъект) операции. - array [0..n_operations] of 1..n_moving_obj : main_obj_of_operation; + array [0..n_operations] of 0..n_moving_obj : main_obj_of_operation; % Является ли операция швартовкой/отшвартовкой. array [0..n_operations] of bool : is_mooring_op; @@ -115,11 +123,11 @@ array [1..n_operations] of 1..n_locations : operations_destination; % Локац ); % Начальное состояние. - array [1..n_moving_obj] of 0..n_locations : initial_m_obj_loc; + array [0..n_moving_obj] of 0..n_locations : initial_m_obj_loc; constraint forall (i in 1..n_moving_obj) (m_obj_loc[i, 0] = initial_m_obj_loc[i]); % Конечное состояние. - array [1..n_moving_obj] of 0..n_locations : final_m_obj_loc; + array [0..n_moving_obj] of 0..n_locations : final_m_obj_loc; constraint forall (i in 1..n_moving_obj) ( (final_m_obj_loc[i] != 0) -> (m_obj_loc[i, n_intervals + 1] = final_m_obj_loc[i]) @@ -133,13 +141,15 @@ array [1..n_operations] of 1..n_locations : operations_destination; % Локац % Связь ресурсов с операцией и основным объектом. % Операции, которые могут задействовать данный объект как ресурс. - array [1..n_moving_obj] of set of 1..n_operations : operations_that_used_obj_as_resource; + array [0..n_moving_obj] of set of 1..n_operations : operations_that_used_obj_as_resource; % Является ли данная операция операцией перемещения. array [0..n_operations] of bool : is_moving_operation; % Операция, в которой участвует данный объект как ресурс. - array [1..n_moving_obj, 0..(n_intervals + 1)] of var 0..n_operations : participation_as_resource; + array [0..n_moving_obj, 0..(n_intervals + 1)] of var 0..n_operations : participation_as_resource; + + constraint forall (t in 0..(n_intervals + 1)) (participation_as_resource[0, t] = 0); % Фиктивный объект. % Граничные значения. constraint forall (obj in 1..n_moving_obj) (participation_as_resource[obj, 0] = 0); @@ -187,7 +197,7 @@ array [1..n_operations] of 1..n_locations : operations_destination; % Локац array [1..n_resources_counters] of 1..n_resources_types : counter_type; % Типы ресурсов, за которыми следят счётчики. array [1..n_resources_counters] of 1..n_operations : operation_of_counter; % Операция, которой принадлежит данный счётчик. array [1..n_resources_counters] of int : required_counter_values; % Необходимые значения на счётчиках ресурсов для выполнения операции. - array [1..n_operations] of set of 1..n_resources_counters : counters_of_operation; % Счётчики, которые относятся к данной операции. + array [0..n_operations] of set of 1..n_resources_counters : counters_of_operation; % Счётчики, которые относятся к данной операции. % Участие всех необходимых ресурсов в операции. constraint forall (t in 1..n_intervals, op in 1..n_operations, counter in counters_of_operation[op]) ( @@ -202,20 +212,22 @@ array [1..n_operations] of 1..n_locations : operations_destination; % Локац ); % Участие объекта в операциях грузообработки. - array [1..n_moving_obj, 1..n_intervals] of var bool : is_involved_in_cargo_op; + array [0..n_moving_obj, 1..n_intervals] of var bool : is_involved_in_cargo_op; + + constraint forall (t in 1..n_intervals) (is_involved_in_cargo_op[0, t] = false); % Фиктивный объект. - array [1..n_moving_obj] of set of 1..n_operations : related_cargo_op_using_obj_as_main; + % Операции погрузки, которые используют этот объект в качестве главного или в качестве бункеровщика. + array [0..n_moving_obj] of set of 0..n_operations : related_cargo_op; % TODO узнать про where % Определение is_involved_in_cargo_op. constraint forall (obj in 1..n_moving_obj, t in 1..n_intervals) ( - is_involved_in_cargo_op[obj, t] = - % (exists (op in 1..n_operations where (not is_moving_operation[op]) /\ (main_obj_of_operation[op] = obj)) ( - (exists (op in related_cargo_op_using_obj_as_main[obj]) ( - op_status[op, t] - )) - \/ - ((participation_as_resource[obj, t] != 0) /\ (not is_moving_operation[participation_as_resource[obj, t]])) + is_involved_in_cargo_op[obj, t] = ( + % (exists (op in 1..n_operations where (not is_moving_operation[op]) /\ (main_obj_of_operation[op] = obj)) ( + (exists (op in related_cargo_op[obj]) (op_status[op, t])) + \/ + ((participation_as_resource[obj, t] != 0) /\ (not is_moving_operation[participation_as_resource[obj, t]])) + ) ); % Операции перемещения исключают операции грузообработки и наоборот. constraint forall (obj in 1..n_moving_obj, t in 1..n_intervals) ( @@ -226,35 +238,41 @@ array [1..n_operations] of 1..n_locations : operations_destination; % Локац % Требуемое положение конкретных типов объектов в момент начала операций. array [1..n_resources_counters] of 1..n_locations : operations_resources_start_loc; - % Требуемое положение главного объекта в момент начала операций. - array [1..n_operations] of 1..n_locations : main_obj_start_loc; - - % Наличие и готовность всех ресурсов. + % Наличие на месте всех ресурсов. constraint forall ( op in 1..n_operations, t in 1..n_intervals, counter in counters_of_operation[op], obj in objects_of_type[counter_type[counter]]) ( ((participation_as_resource[obj, t] = op) /\ (op_start[op, t]) - ) -> ((m_obj_loc[obj, t] == operations_resources_start_loc[counter]) /\ - (not is_m_obj_in_movement_before_start[obj, t]) /\ - ((not is_moving_operation[op]) -> (current_moving_operation[obj, t] = 0)) - ) + ) -> (m_obj_loc[obj, t] == operations_resources_start_loc[counter]) + ); + + % Требуемое положение главного объекта в момент начала операций. + array [0..n_operations] of 0..n_locations : main_obj_start_loc; + % Требуемое положение бункеровщика в момент начала операции. + array [0..n_operations] of 0..n_locations : bunker_start_loc; + + % Бункеровщик, участвующий в операции погрузки (0, если такогого нет или это не погрузка). + array [0..n_operations] of 0..n_moving_obj : bunker_of_cargo_op; + + % Наличие главных объектов на месте. + constraint forall (op in 1..n_operations, + t in 1..n_intervals, + obj = main_obj_of_operation[op]) ( + op_start[op, t] -> (m_obj_loc[obj, t] == main_obj_start_loc[op]) ); - /* TODO узнать, как ((not is_moving_operation[op]) -> (current_moving_operation[obj, t] = 0)) влияет на производительность - - по идее это не нужно, но на v2-60 без неё гораздо лучше чем с ней. - */ - - % Наличие главных объектов (субъектов) на месте. - constraint forall (op in 1..n_operations, t in 1..n_intervals, obj = main_obj_of_operation[op]) ( - op_start[op, t] -> ((m_obj_loc[obj, t] == main_obj_start_loc[op]) /\ - (not is_m_obj_in_movement_before_start[obj, t]) - ) + + % Наличие бункеровщиков на месте. + constraint forall (op in 1..n_operations, + t in 1..n_intervals, + obj = bunker_of_cargo_op[op] where obj != 0) ( + op_start[op, t] -> (m_obj_loc[obj, t] == bunker_start_loc[op]) ); % Непрерывность перемещения и швартовки. - array [1..n_operations] of int : operations_duration; - array [1..n_operations] of bool : is_continuous_operation; + array [0..n_operations] of int : operations_duration; + array [0..n_operations] of bool : is_continuous_operation; constraint forall (i in 1..n_operations, len = operations_duration[i] where is_continuous_operation[i]) ( (forall (j in 1..(n_intervals - len + 1)) ( @@ -268,14 +286,14 @@ array [1..n_operations] of 1..n_locations : operations_destination; % Локац ); % Конфликтующие операции. - array [1..n_operations] of set of 1..n_operations : conflicting_operations; + array [0..n_operations] of set of 1..n_operations : conflicting_operations; constraint forall (op in 1..n_operations, t in 0..(n_intervals + 1), conf_op in conflicting_operations[op]) ( op_status[op, t] -> not op_status[conf_op, t] ); % Окна непогоды. - array [1..n_operations, 1..n_intervals] of bool : bad_weather; + array [0..n_operations, 0..n_intervals] of bool : bad_weather; constraint forall (op in 1..n_operations, t in 1..n_intervals) ( bad_weather[op, t] -> (op_status[op, t] = false) @@ -284,10 +302,12 @@ array [1..n_operations] of 1..n_locations : operations_destination; % Локац % Грузообработка. int : n_cargo_types; int : n_obj_with_storage; - array [1..n_obj_with_storage, 0..(n_intervals + 1), 1..n_cargo_types] of var int : storage_used_volume; % Первые n_moving_obj соответствуют наполненности соответствующих движущихся объектов. + array [0..n_obj_with_storage, 0..(n_intervals + 1), 1..n_cargo_types] of var int : storage_used_volume; % Первые n_moving_obj соответствуют наполненности соответствующих движущихся объектов. + + constraint forall (t in 0..(n_intervals + 1), cargo in 1..n_cargo_types) (storage_used_volume[0, t, cargo] = 0); % Фиктивный объект. % Ограничения на вместимость. - array [1..n_obj_with_storage] of int : max_storage_vol; + array [0..n_obj_with_storage] of int : max_storage_vol; % Максимальный объём. constraint forall (storage in 1..n_obj_with_storage, t in 0..(n_intervals + 1)) ( sum (cargo in 1..n_cargo_types) (storage_used_volume[storage, t, cargo]) <= max_storage_vol[storage] @@ -298,8 +318,8 @@ array [1..n_operations] of 1..n_locations : operations_destination; % Локац ); % Ограничения на граничные значения. - array [1..n_obj_with_storage, 1..n_cargo_types] of int : initial_storage_vol; - array [1..n_obj_with_storage, 1..n_cargo_types] of int : final_storage_vol; + array [0..n_obj_with_storage, 0..n_cargo_types] of int : initial_storage_vol; + array [0..n_obj_with_storage, 0..n_cargo_types] of int : final_storage_vol; constraint forall (storage in 1..n_obj_with_storage, cargo in 1..n_cargo_types) ( % Initial values. storage_used_volume[storage, 0, cargo] = initial_storage_vol[storage, cargo] @@ -311,14 +331,14 @@ array [1..n_operations] of 1..n_locations : operations_destination; % Локац ); % Изменение грузов в хранилищах. - array [1..n_obj_with_storage, 0..(n_intervals + 1), 1..n_cargo_types] of int : cargo_flows; + array [0..n_obj_with_storage, 0..(n_intervals + 1), 1..n_cargo_types] of int : cargo_flows; int : n_loading_op; - array [1..n_operations] of int : loading_op_delta_of_main_obj; - array [1..n_operations] of 1..n_obj_with_storage : operations_main_stor; - array [1..n_operations] of 1..n_obj_with_storage : operations_secondary_stor; - array [1..n_operations] of 1..n_cargo_types : operations_cargo_t; + array [0..n_operations] of int : loading_op_delta_of_main_obj; + array [0..n_operations] of 1..n_obj_with_storage : operations_main_stor; + array [0..n_operations] of 1..n_obj_with_storage : operations_secondary_stor; + array [0..n_operations] of 1..n_cargo_types : operations_cargo_t; array [1..n_loading_op] of int : loading_op_delta; array [1..n_loading_op] of 1..n_operations : loading_op_n; % Номера среди общего списка операций. @@ -343,7 +363,7 @@ array [1..n_operations] of 1..n_locations : operations_destination; % Локац array [1..n_fixed_op] of 1..n_intervals : fixed_op_start; array [1..n_fixed_op] of 1..n_intervals : fixed_op_end; % Включительно. - array [1..n_operations, 1..n_intervals] of bool : is_fixed; + array [0..n_operations, 0..n_intervals] of bool : is_fixed; constraint forall (no in 1..n_fixed_op, op = fixed_op[no]) ( forall (t in fixed_op_start[no]..fixed_op_end[no]) ( @@ -356,7 +376,9 @@ array [1..n_operations] of 1..n_locations : operations_destination; % Локац % Оптимизация - сдвиг в начало. % Возможно ли начать операцию в данный интервал (если предположить, что операция длится 1 интервал). - array [1..n_operations, 1..n_intervals] of var bool : is_op_possible; + array [0..n_operations, 1..n_intervals] of var bool : is_op_possible; + + constraint forall (t in 1..n_intervals) (is_op_possible[0, t] = false); % Фиктивный объект. % Счётчик ресурсов, которые могут быть потенциально задействованы в операции. array [1..n_resources_counters , 1..n_intervals] of var int : possible_resources_counter; @@ -371,7 +393,9 @@ array [1..n_operations] of 1..n_locations : operations_destination; % Локац ); % Достаточно ли свободных ресурсов для операции. - array [1..n_operations, 1..n_intervals] of var bool : is_enough_free_resources; + array [0..n_operations, 1..n_intervals] of var bool : is_enough_free_resources; + + constraint forall (t in 1..n_intervals) (is_enough_free_resources[0, t] = false); % Фиктивный объект. constraint forall (op in 1..n_operations, t in 1..n_intervals) ( is_enough_free_resources[op, t] = forall (counter in counters_of_operation[op]) ( @@ -379,12 +403,6 @@ array [1..n_operations] of 1..n_locations : operations_destination; % Локац ) ); - array [1..n_operations, 1..n_intervals] of var bool : debug_1; - constraint forall (op in 1..n_operations, t in 1..n_intervals) ( - debug_1[op, t] = - (m_obj_loc[main_obj_of_operation[op], t] = main_obj_start_loc[op]) % Главный объект на месте. - ); - % Определение is_op_possible. constraint forall (op in 1..n_operations, t in 1..n_intervals) ( is_op_possible[op, t] = ( @@ -400,7 +418,7 @@ array [1..n_operations] of 1..n_locations : operations_destination; % Локац /\ % Если это операция перемещения, то главный объект % не участвует в операциях погрузки. ((is_mooring_op[op] /\ (operations_destination[op] mod 2 = 0)) -> ( - obj_in_loc_counter[operations_destination[op], t] = 0) + current_moored_obj[operations_destination[op], t] = 0) ) /\ % Если это операция пришвартовки, то в этот интервал % причал должен быть свободным. ((not is_moving_operation[op]) -> ( @@ -417,9 +435,16 @@ array [1..n_operations] of 1..n_locations : operations_destination; % Локац % объём берегового хранилища и хранилища судна за % границы дозволенного. (((not is_moving_operation[op]) /\ (main_obj_start_loc[op] mod 2 = 1)) -> - (obj_in_loc_counter[twin_location[main_obj_start_loc[op]], t] = 0) - ) % Если это операция грузообработки без пришвартовки, - % то причал должен быть свободен в этот интервал. + ((current_moored_obj[twin_location[main_obj_start_loc[op]], t] = 0) /\ + (current_moored_obj[twin_location[main_obj_start_loc[op]], t] = main_obj_of_operation[op])) + ) /\ % Если это операция грузообработки без пришвартовки, + % то причал должен быть свободен в этот интервал, либо + % главный объект должен быть уже условно пришвартован. + (((bunker_of_cargo_op[op] != 0) /\ (op_status[op, t])) -> + ((m_obj_loc[bunker_of_cargo_op[op], t] = bunker_start_loc[op]) /\ + (current_moving_operation[bunker_of_cargo_op[op], t] = 0)) + ) % Бункеровщик (если есть) находится в нужной локации + % и не участвует в операциях перемещения. ) ); @@ -450,13 +475,16 @@ output [show(sum(is_not_terminated)), "\n", "op_start = ", show(op_start), "\n\n", "is_not_terminated = ", show(is_not_terminated), "\n\n", "storage_used_volume = ", show(storage_used_volume), "\n\n", - "is_m_obj_in_movement_before_start = ", show(is_m_obj_in_movement_before_start), "\n\n", - "obj_in_loc_counter = ", show(obj_in_loc_counter), "\n\n", "m_obj_loc = ", show(m_obj_loc), "\n\n", "current_moving_operation = ", show(current_moving_operation), "\n\n", "resources_counter {", show(n_intervals), "} = ", show(resources_counter), "\n\n", "operation_of_counter {", show(n_resources_counters), "} = ", show(operation_of_counter), "\n\n", "participation_as_resource = ", show(participation_as_resource), "\n\n", + + "is_involved_in_cargo_op = {", show(n_intervals), "} ", show(is_involved_in_cargo_op), "\n\n", + + "current_moored_obj = ", show(current_moored_obj), "\n\n", + /* "is_op_possible {", show(n_intervals), "} = ", show(is_op_possible), "\n\n", "debug_1 {", show(n_intervals), "} = ", show(debug_1), "\n\n", diff --git a/src/inport/Bunker.java b/src/inport/Bunker.java index 3be1104..d139204 100644 --- a/src/inport/Bunker.java +++ b/src/inport/Bunker.java @@ -8,26 +8,17 @@ package inport; * * @author topazh_ag */ -public class Bunker extends MovingObject { +public class Bunker extends TransportShip { - public Bunker(int id, String name) { - super(id, name); + public Bunker(int id, String name, double cargoMax) { + super(id, name, cargoMax); } public Bunker() { super( ); } - - @Override - public String toString() { - return getId() + ";" + getName(); - } - public Bunker(String s) { super(s); } - - - } diff --git a/src/inport/ConversionUtil.java b/src/inport/ConversionUtil.java index 8f5bd5e..215e01c 100644 --- a/src/inport/ConversionUtil.java +++ b/src/inport/ConversionUtil.java @@ -19,10 +19,17 @@ public class ConversionUtil { } } + static private void write2DArrayOfInt(FileWriter writer, + String name, + ArrayList> operations) throws IOException { + locWrite2DArray(writer, name, operations, Object::toString, false); + } + static private void write2DArrayOfInt(FileWriter writer, String name, - ArrayList> operations) throws IOException { - locWrite2DArray(writer, name, operations, Object::toString); + ArrayList> operations, + boolean isNumberedFromZero) throws IOException { + locWrite2DArray(writer, name, operations, Object::toString, isNumberedFromZero); } static private void write2DArrayOfSet(FileWriter writer, @@ -47,39 +54,61 @@ public class ConversionUtil { ); } - static private void write2DArrayOfSetAs3DArray(FileWriter writer, + static private > void write2DArrayOfSetAs3DArray(FileWriter writer, + String name, + ArrayList> operations) throws IOException { + write2DArrayOfSetAs3DArray(writer, name, operations, false); + } + + static private > void write2DArrayOfSetAs3DArray(FileWriter writer, String name, - ArrayList>> operations) throws IOException { + ArrayList> operations, + boolean isNumberedFromZero) throws IOException { int maxSize = 0; ArrayList> sizes = new ArrayList<>(); int dim1 = operations.size(); int dim2 = 0; - for (ArrayList> a1 : operations) { + for (ArrayList a1 : operations) { dim2 = a1.size(); ArrayList locSizes = new ArrayList<>(); - for (ArrayList a2 : a1) { + for (T a2 : a1) { maxSize = Math.max(maxSize, a2.size()); locSizes.add(a2.size()); } sizes.add(locSizes); } writer.write(name + "_max_size = " + maxSize + ";\n"); - write2DArrayOfInt(writer, name + "_sizes", sizes); + write2DArrayOfInt(writer, name + "_sizes", sizes, true); + + { + String firstEl = Integer.toString(isNumberedFromZero ? 0 : 1); + int modifier = isNumberedFromZero ? -1 : 0; + writer.write(name + " = array3d(" + firstEl + ".." + (dim1 + modifier) + + ", " + firstEl + ".." + (dim2 + modifier) + + ", 1.." + name + "_max_size, ["); + } - writer.write(name + " = array3d(1.." + dim1 + ", 1.." + dim2 + ", 1.." + name + "_max_size, ["); boolean isFirst = true; - for (ArrayList> a1 : operations) { - for (ArrayList a2 : a1) { - for (int i = 0; i < maxSize; i++) { + for (ArrayList a1 : operations) { + for (T a2 : a1) { + for (Integer val : a2) { + if (isFirst) { + isFirst = false; + } else { + writer.write(", "); + } + writer.write(Integer.toString(val)); + } + for (int i = a2.size(); i < maxSize; i++) { if (isFirst) { isFirst = false; } else { writer.write(", "); } - writer.write(Integer.toString(i < a2.size() ? a2.get(i) : 1)); + writer.write(Integer.toString(1)); } } } @@ -139,31 +168,40 @@ public class ConversionUtil { writer.write("]);\n"); } + static private void locWrite2DArray(FileWriter writer, + String name, + ArrayList> operations, + Function toMZNFormat) throws IOException { + locWrite2DArray(writer, name, operations, toMZNFormat, false); + } + + static int boolToInt(boolean b) { + return b ? 1 : 0; + } + static private void locWrite2DArray(FileWriter writer, String name, ArrayList> operations, - Function toMZNFormat) throws IOException { - writer.write(name + " = \n"); + Function toMZNFormat, + boolean isNumberedFromZero) throws IOException { + + writer.write(name + " = array2d(" + (isNumberedFromZero ? 0 : 1) + ".." + (operations.size() - boolToInt(isNumberedFromZero)) + + ", " + (isNumberedFromZero ? 0 : 1) + ".." + (operations.get(0).size() - boolToInt(isNumberedFromZero)) + + ", ["); + boolean isFirst0 = true; + for (ArrayList a : operations) { - if (isFirst0) { - isFirst0 = false; - writer.write(" [| "); - } else { - writer.write(" | "); - } - boolean isFirst1 = true; for (T val : a) { - if (isFirst1) { - isFirst1 = false; + if (isFirst0) { + isFirst0 = false; } else { writer.write(", "); } writer.write(toMZNFormat.apply(val)); } - writer.write("\n"); } - writer.write(" |];\n"); + writer.write("]);\n"); } static private void writeArray(FileWriter writer, @@ -362,6 +400,10 @@ public class ConversionUtil { private ArrayList> objectsOfType; + public ArrayList getMovingObjects() { + return movingObjects; + } + Task(TaskCase task, String fileName) { this.fileName = fileName; this.task = task; @@ -375,19 +417,11 @@ public class ConversionUtil { locationNumberById.put(new Pair<>(berth.getId(), true), locationNumberById.size()); } - movingObjects = new ArrayList<>(); + movingObjects = calcMovingObjects(task); mObjNumberById = new TreeMap<>(); - for (MovingObject obj : task.getShips()) { - mObjNumberById.put(obj.getId(), movingObjects.size()); - movingObjects.add(obj); - } - for (MovingObject obj : task.getTows()) { - mObjNumberById.put(obj.getId(), movingObjects.size()); - movingObjects.add(obj); - } - for (MovingObject obj : task.getEquipments()) { - mObjNumberById.put(obj.getId(), movingObjects.size()); - movingObjects.add(obj); + + for (int i = 0; i < movingObjects.size(); i++) { + mObjNumberById.put(movingObjects.get(i).getId(), i); } operationTemplates = new ArrayList<>(task.getTemplates()); @@ -417,6 +451,10 @@ public class ConversionUtil { typeToN.put(type, next); next++; } + for (Integer type : task.getBunkerTypes().keySet()) { + typeToN.put(type, next); + next++; + } for (Integer type : task.getEquipmentsTypes().keySet()) { typeToN.put(type, next); next++; @@ -508,7 +546,7 @@ public class ConversionUtil { for (MovingObjectState state : task.getVesselInitialState()) { initialStates.set(mObjToN(state.getVessel()), getLocNById(state.getLocation().getId(), false)); } - writeArray(writer, "initial_m_obj_loc", initialStates, (Integer p) -> p + 1); + writeArray(writer, "initial_m_obj_loc", initialStates, (Integer p) -> p + 1, Optional.of(-1)); writer.write("\n"); } @@ -538,23 +576,27 @@ public class ConversionUtil { /* Окна погоды. Новый формат. */ private void weatherWindowsNewFormat() throws IOException { ArrayList> badWeather = new ArrayList<>(); + badWeather.add(new ArrayList<>()); + for (int j = 0; j <= n_intervals; j++) { + badWeather.get(0).add(false); + } for (int i = 0; i < operationTemplates.size(); i++) { ArrayList curLine = new ArrayList<>(); - for (int j = 0; j < n_intervals; j++) { + for (int j = 0; j <= n_intervals; j++) { curLine.add(false); } operationTemplates.get(i).getTimeWindows().forEach( (Double start, Double duration) -> { for (int j = (int)Math.floor(start); j < (int)Math.ceil(start + duration); j++) { - curLine.set(j, true); + curLine.set(j + 1, true); } } ); badWeather.add(curLine); } - locWrite2DArray(writer, "bad_weather", badWeather, Objects::toString); + locWrite2DArray(writer, "bad_weather", badWeather, Objects::toString, true); } /* Непрерывность перемещения и швартовки. */ @@ -576,8 +618,8 @@ public class ConversionUtil { isMovingObj.add(false); } } - writeArray(writer, "operations_duration", operationsDuration); - writeArray(writer, "is_continuous_operation", isMovingObj); + writeArray(writer, "operations_duration", operationsDuration, Optional.of(1)); + writeArray(writer, "is_continuous_operation", isMovingObj, Optional.of(false)); } /* Конечные положения объектов. */ @@ -586,7 +628,7 @@ public class ConversionUtil { for (MovingObjectState state : task.getVesselEndState()) { finalStates.set(mObjToN(state.getVessel()), getLocNById(state.getLocation().getId(), false)); } - writeArray(writer, "final_m_obj_loc", finalStates, (Integer p) -> p + 1); + writeArray(writer, "final_m_obj_loc", finalStates, (Integer p) -> p + 1, Optional.of(-1)); } /* Наличие всех ресурсов на месте, в том числе и самого корабля. */ @@ -658,7 +700,7 @@ public class ConversionUtil { for (Storage storage : storages) { maxStorageVol.add((int)Math.ceil(storage.getVolume())); } - writeArray(writer, "max_storage_vol", maxStorageVol); + writeArray(writer, "max_storage_vol", maxStorageVol, Optional.of(0)); writer.write("\n"); } @@ -668,9 +710,9 @@ public class ConversionUtil { ArrayList> initialStorageVol = new ArrayList<>(); ArrayList> finalStorageVol = new ArrayList<>(); - for (int i = 0; i < nObjWithStorage; i++) { - initialStorageVol.add(integerArray(cargoes.size(), 0)); - finalStorageVol .add(integerArray(cargoes.size(), -1)); + for (int i = 0; i <= nObjWithStorage; i++) { + initialStorageVol.add(integerArray(cargoes.size() + 1, 0)); + finalStorageVol .add(integerArray(cargoes.size() + 1, -1)); } for (StorageState st : task.getStorageInitialState()) { int cargoN = cargoNById.get(st.getCargo().getId()); @@ -679,10 +721,10 @@ public class ConversionUtil { if (st.getStorage() instanceof Storage) { Storage storage = (Storage) st.getStorage(); int stN = storNById.get(storage.getId()); - initialStorageVol.get(movingObjects.size() + stN).set(cargoN, val); + initialStorageVol.get(movingObjects.size() + stN + 1).set(cargoN + 1, val); } else if (st.getStorage() instanceof TransportShip) { TransportShip ship = (TransportShip) st.getStorage(); - initialStorageVol.get(mObjToN(ship)).set(cargoN, val); + initialStorageVol.get(mObjToN(ship) + 1).set(cargoN + 1, val); } } for (StorageState st : task.getStorageEndState()) { @@ -692,14 +734,14 @@ public class ConversionUtil { if (st.getStorage() instanceof Storage) { Storage storage = (Storage) st.getStorage(); int stN = storNById.get(storage.getId()); - finalStorageVol.get(movingObjects.size() + stN).set(cargoN, val); + finalStorageVol.get(movingObjects.size() + stN + 1).set(cargoN + 1, val); } else if (st.getStorage() instanceof TransportShip) { TransportShip ship = (TransportShip) st.getStorage(); - finalStorageVol.get(mObjToN(ship)).set(cargoN, val); + finalStorageVol.get(mObjToN(ship) + 1).set(cargoN + 1, val); } } - write2DArrayOfInt(writer, "initial_storage_vol", initialStorageVol); - write2DArrayOfInt(writer, "final_storage_vol", finalStorageVol); + write2DArrayOfInt(writer, "initial_storage_vol", initialStorageVol, true); + write2DArrayOfInt(writer, "final_storage_vol", finalStorageVol, true); } /* Потоки грузов. */ @@ -721,8 +763,19 @@ public class ConversionUtil { cargoFlows.get(storageN + movingObjects.size()).get(i).set(cargoN, (int)flow.getCurrentValue(i - 0.1)); } } - writer.write("cargo_flows = array3d(1..n_obj_with_storage, 0..(n_intervals + 1), 1..n_cargo_types, ["); + writer.write("cargo_flows = array3d(0..n_obj_with_storage, 0..(n_intervals + 1), 1..n_cargo_types, ["); boolean isFirst = true; + + for (int j = 0; j < n_intervals + 2; j++) { + for (int k = 0; k < cargoes.size(); k++) { + if (isFirst) { + isFirst = false; + } else { + writer.write(", "); + } + writer.write("0"); + } + } for (int i = 0; i < nObjWithStorage; i++) { for (int j = 0; j < n_intervals + 2; j++) { for (int k = 0; k < cargoes.size(); k++) { @@ -749,6 +802,9 @@ public class ConversionUtil { ArrayList operations_secondary_stor = new ArrayList<>(); ArrayList operations_cargo_t = new ArrayList<>(); + ArrayList bunker_of_cargo_op = new ArrayList<>(); + ArrayList is_bunker_required = new ArrayList<>(); + for (int i = 0; i < nObjWithStorage; i++) { involvedOperations.add(new ArrayList<>()); for (int j = 0; j < cargoes.size(); j++) { @@ -759,13 +815,19 @@ public class ConversionUtil { for (int i = 0; i < operationTemplates.size(); i++) { if (operationTemplates.get(i) instanceof LoadingTemplate) { LoadingTemplate op = (LoadingTemplate) operationTemplates.get(i); - int storageN = storNById.get(op.getStorage().getId()); + + int storageN; + if (op.getBunker().isPresent()) { + storageN = mObjToN(op.getBunker().get()); + } else { + storageN = storNById.get(op.getStorage().getId()) + movingObjects.size(); + } int shipN = mObjToN(op.getLoader()); int cargoN = cargoNById.get(op.getCargo().getId()); loadingOpDelta.add(-(int)op.getIntensity()); loadingOpN.add(i); - involvedOperations.get(storageN + movingObjects.size()).get(cargoN).add(loadingOpDelta.size()); + involvedOperations.get(storageN).get(cargoN).add(loadingOpDelta.size()); loadingOpDelta.add((int)op.getIntensity()); loadingOpN.add(i); @@ -773,13 +835,15 @@ public class ConversionUtil { loading_op_delta_of_main_obj.add((int)op.getIntensity()); operations_main_stor.add(shipN + 1); - operations_secondary_stor.add(storageN + movingObjects.size() + 1); + operations_secondary_stor.add(storageN + 1); operations_cargo_t.add(cargoN + 1); + bunker_of_cargo_op.add(op.getBunker().isPresent() ? mObjToN(op.getBunker().get()) + 1 : 0); } else { loading_op_delta_of_main_obj.add(0); operations_main_stor.add(1); operations_secondary_stor.add(1); operations_cargo_t.add(1); + bunker_of_cargo_op.add(0); } } writer.write("n_loading_op = " + loadingOpDelta.size() + ";\n"); @@ -791,10 +855,11 @@ public class ConversionUtil { writeArray(writer, "loading_op_delta", loadingOpDelta); writeArray(writer, "loading_op_n", loadingOpN, (Integer i) -> i + 1); - writeArray(writer, "loading_op_delta_of_main_obj", loading_op_delta_of_main_obj); - writeArray(writer, "operations_main_stor", operations_main_stor); - writeArray(writer, "operations_secondary_stor", operations_secondary_stor); - writeArray(writer, "operations_cargo_t", operations_cargo_t); + writeArray(writer, "loading_op_delta_of_main_obj", loading_op_delta_of_main_obj, Optional.of(0)); + writeArray(writer, "operations_main_stor", operations_main_stor, Optional.of(1)); + writeArray(writer, "operations_secondary_stor", operations_secondary_stor, Optional.of(1)); + writeArray(writer, "operations_cargo_t", operations_cargo_t, Optional.of(1)); + writeArray(writer, "bunker_of_cargo_op", bunker_of_cargo_op, Optional.of(0)); writer.write("\n"); } @@ -925,57 +990,48 @@ public class ConversionUtil { } private void movingObjectLocationDefinition(boolean isV1) throws IOException { - if (! isV1) { - writeArray(writer, "is_mooring_op", isMooringOp, Optional.of(false)); - writeArray(writer, "main_obj_of_operation", mainObjOfOperation, (Integer val) -> val + 1, Optional.of(1)); - - ArrayList> locMovingOpOfObj = new ArrayList<>(); - for (ArrayList operations : movingOpOfObj) { - ArrayList lOperations = new ArrayList<>(); - for (int i = 0; i < operationTemplates.size(); i++) { - lOperations.add(false); - } - for (Integer op : operations) { - lOperations.set(op - 1, true); - } - locMovingOpOfObj.add(lOperations); - } - locWrite2DArray(writer, "moving_op_of_obj", locMovingOpOfObj, Objects::toString); - } else { - writeArrayOfSetAs2DArray(writer, "moving_op_of_obj", movingOpOfObj, true, false); - } - writeArray(writer, "operations_destination", operationsDestination, (Integer val) -> val + 1); + writeArray(writer, "is_mooring_op", isMooringOp, Optional.of(false)); + writeArray(writer, "operations_destination", operationsDestination, (Integer val) -> val + 1, + Optional.of(-1)); writer.write("\n"); } - private void unmooredCargoOp() throws IOException { - ArrayList> relatedUnmooredCargoOp = new ArrayList<>(); - for (int i = 0; i < locationNumberById.size(); i++) { - relatedUnmooredCargoOp.add(new TreeSet<>()); + private void cargoOpUsingObj() throws IOException { + ArrayList> relatedCargoOp = new ArrayList<>(); + for (int i = 0; i < movingObjects.size(); i++) { + relatedCargoOp.add(new TreeSet<>()); } for (int i = 0; i < operationTemplates.size(); i++) { if (operationTemplates.get(i) instanceof LoadingTemplate) { LoadingTemplate op = (LoadingTemplate) operationTemplates.get(i); - if (! op.getWithMooring()) { - relatedUnmooredCargoOp.get(getLocNById(op.getStartLocation().getId(), false)).add(i + 1); + relatedCargoOp.get(mObjToN(op.getLoader())).add(i + 1); + if (op.getBunker().isPresent()) { + relatedCargoOp.get(mObjToN(op.getBunker().get())).add(i + 1); } } } - writeArray(writer, "related_unmoored_cargo_op", relatedUnmooredCargoOp, ConversionUtil::setToString); + writeArray(writer, "related_cargo_op", relatedCargoOp, ConversionUtil::setToString, + Optional.of(new TreeSet<>())); } - private void cargoOpUsingObjAsMain() throws IOException { - ArrayList> relatedCargoOpUsingObjAsMain = new ArrayList<>(); - for (int i = 0; i < movingObjects.size(); i++) { - relatedCargoOpUsingObjAsMain.add(new TreeSet<>()); + private void addOpWithNominallyMooring() throws IOException { + ArrayList>> opWithNominallyMooring = new ArrayList<>(); + for (int loc = 0; loc <= locationNumberById.size(); loc++) { + opWithNominallyMooring.add(new ArrayList<>()); + for (int obj = 0; obj <= movingObjects.size(); obj++) { + opWithNominallyMooring.get(loc).add(new TreeSet<>()); + } } for (int i = 0; i < operationTemplates.size(); i++) { - if (operationTemplates.get(i) instanceof LoadingTemplate) { - LoadingTemplate op = (LoadingTemplate) operationTemplates.get(i); - relatedCargoOpUsingObjAsMain.get(getMObjNumberById().get(op.getLoader().getId())).add(i + 1); + OperationTemplate t = operationTemplates.get(i); + if ((t instanceof MooringTemplate) || + ((t instanceof LoadingTemplate) && (! ((LoadingTemplate)t).getWithMooring()))) { + opWithNominallyMooring.get(getLocNById(t.getStartLocation().getId(), true) + 1) + .get(mObjToN(getExecutor(t)) + 1) + .add(i + 1); } } - writeArray(writer, "related_cargo_op_using_obj_as_main", relatedCargoOpUsingObjAsMain, ConversionUtil::setToString); + write2DArrayOfSetAs3DArray(writer, "op_with_nominally_mooring", opWithNominallyMooring, true); } void portToMiniZinc_0() throws IOException { @@ -1054,22 +1110,29 @@ public class ConversionUtil { } } + public static ArrayList calcMovingObjects(TaskCase taskCase) { + ArrayList movingObjects = new ArrayList<>(); + movingObjects.addAll(taskCase.getShips()); + movingObjects.addAll(taskCase.getBunkers()); + movingObjects.addAll(taskCase.getTows()); + movingObjects.addAll(taskCase.getEquipments()); + return movingObjects; + } + /* Каждую операцию лишает типизации по главному действующему лицу. */ public static ArrayList renumberOperations(TaskCase task) { TreeMap> shipsByType = new TreeMap<>(); for (Integer type : task.getVesselTypes().keySet()) { shipsByType.put(type, new ArrayList<>()); } + for (Integer type : task.getBunkerTypes().keySet()) { + shipsByType.put(type, new ArrayList<>()); + } for (Integer type : task.getEquipmentsTypes().keySet()) { shipsByType.put(type, new ArrayList<>()); } - ArrayList movingObjects = new ArrayList<>(); - movingObjects.addAll(task.getTows()); - movingObjects.addAll(task.getShips()); - movingObjects.addAll(task.getEquipments()); - - for (MovingObject obj : movingObjects) { + for (MovingObject obj : calcMovingObjects(task)) { if (obj.getType().isPresent()) { shipsByType.get(obj.getType().getAsInt()).add(obj); } @@ -1082,8 +1145,34 @@ public class ConversionUtil { if (t.getLoaderType().isPresent()) { for (MovingObject obj : shipsByType.get(t.getLoaderType().getAsInt())) { assert(obj instanceof TransportShip); - result.add(new LoadingTemplate((TransportShip)obj, t.getStartLocation(), t.getStorage(), - t.getCargo(), t.getResourcesTypes(), t.getWithMooring(), t.getIntensity(), t.getId())); + if (t.getBunkerType().isPresent()) { + for (MovingObject bunker : shipsByType.get(t.getBunkerType().getAsInt())) { + assert(obj instanceof Bunker); + result.add(new LoadingTemplate( + (TransportShip)obj, + t.getStartLocation(), + t.getStorage(), + t.getCargo(), + Optional.of((Bunker)bunker), + t.getResourcesTypes(), + t.getWithMooring(), + t.getIntensity(), + t.getId() + )); + } + } else { + result.add(new LoadingTemplate( + (TransportShip)obj, + t.getStartLocation(), + t.getStorage(), + t.getCargo(), + Optional.empty(), + t.getResourcesTypes(), + t.getWithMooring(), + t.getIntensity(), + t.getId() + )); + } } } } @@ -1129,6 +1218,26 @@ public class ConversionUtil { conflictingOperationsG.get(i).add(j + 1); } } + { // Взаимоисключение операций погрузки без швартовки на одном причале с разными субъектами. + // + операций бункеровки без швартовки на одном причалое + // TODO переделать + if ((operationTemplates.get(i) instanceof LoadingTemplate) && + (operationTemplates.get(j) instanceof LoadingTemplate)) { + LoadingTemplate op1 = (LoadingTemplate) operationTemplates.get(i); + LoadingTemplate op2 = (LoadingTemplate) operationTemplates.get(j); + + if ((! op1.getWithMooring()) && (! op2.getWithMooring()) && + (op1.getStartLocation() == op2.getStartLocation()) && + (op1.getLoader() != op2.getLoader())) { + conflictingOperationsG.get(i).add(j + 1); + } + if ((! op1.getWithMooring()) && (! op2.getWithMooring()) && + (op1.getStartLocation() == op2.getStartLocation()) && + (op1.getBunker().isPresent()) && (op2.getBunker().isPresent())) { + conflictingOperationsG.get(i).add(j + 1); + } + } + } { // Взаимоисключение операций перемещения и грузообработки с общим деятелем. // TODO вынести в отдельный constraint OperationTemplate t1 = operationTemplates.get(i); @@ -1147,7 +1256,8 @@ public class ConversionUtil { } } } - writeArray(writer, "conflicting_operations", conflictingOperationsG, ConversionUtil::setToString); + writeArray(writer, "conflicting_operations", conflictingOperationsG, ConversionUtil::setToString, + Optional.of(new TreeSet<>())); } public ArrayList> getIsFixedArray() { @@ -1158,9 +1268,9 @@ public class ConversionUtil { } ArrayList> isFixed = new ArrayList<>(); - for (int i = 0; i < operationTemplates.size(); i++) { + for (int i = 0; i <= operationTemplates.size(); i++) { isFixed.add(new ArrayList<>()); - for (int j = 0; j < n_intervals; j++) { + for (int j = 0; j <= n_intervals; j++) { isFixed.get(i).add(false); } } @@ -1169,7 +1279,7 @@ public class ConversionUtil { int operation = opNoByOpIdAndExecutorNo.get(new Pair<>(op.getTemplate().getId(), op.getExecutor().getId())); for (int i = (int)Math.floor(op.getStart()); i < (int)Math.ceil(op.getDuration() + op.getStart()); i++) { - isFixed.get(operation).set(i, true); + isFixed.get(operation + 1).set(i + 1, true); } } } @@ -1206,7 +1316,36 @@ public class ConversionUtil { writeArray(writer, "fixed_op_resources", fixedOpResources, ConversionUtil::setToString); writeArray(writer, "fixed_op_start", fixedOpStart); writeArray(writer, "fixed_op_end", fixedOpEnd); - locWrite2DArray(writer, "is_fixed", getIsFixedArray(), Objects::toString); + locWrite2DArray(writer, "is_fixed", getIsFixedArray(), Objects::toString, true); + } + + void defDataForCurMovingOp() throws IOException { + ArrayList isMovingOp = new ArrayList<>(); + for (OperationTemplate op : operationTemplates) { + isMovingOp.add((op instanceof MovingTemplate) || (op instanceof MooringTemplate)); + } + writeArray(writer, "is_moving_operation", isMovingOp, Optional.of(false)); + writer.write("\n"); + + writeArray(writer, "main_obj_of_operation", mainObjOfOperation, (Integer val) -> val + 1, Optional.of(-1)); + + ArrayList> locMovingOpOfObj = new ArrayList<>(); + locMovingOpOfObj.add(new ArrayList<>()); + for (int i = 0; i <= operationTemplates.size(); i++) { + locMovingOpOfObj.get(0).add(false); + } + for (ArrayList operations : movingOpOfObj) { + ArrayList lOperations = new ArrayList<>(); + lOperations.add(false); + for (int i = 0; i < operationTemplates.size(); i++) { + lOperations.add(false); + } + for (Integer op : operations) { + lOperations.set(op, true); + } + locMovingOpOfObj.add(lOperations); + } + locWrite2DArray(writer, "moving_op_of_obj", locMovingOpOfObj, Objects::toString, true); } private void typifiedResourcesDefinition() throws IOException { @@ -1223,26 +1362,8 @@ public class ConversionUtil { } } } - writeArray(writer, "operations_that_used_obj_as_resource", res, ConversionUtil::setToString); - writer.write("\n"); - } - { // main_obj_start_loc, is_moving_operation - ArrayList mainObjStartLoc = new ArrayList<>(); - ArrayList isMovingOp = new ArrayList<>(); - - for (OperationTemplate op : operationTemplates) { - boolean isMoored = false; - if ((op instanceof LoadingTemplate) && (((LoadingTemplate) op).getWithMooring())) { - isMoored = true; - } - if ((op instanceof MooringTemplate) && (!((MooringTemplate)op).isDirect())) { - isMoored = true; - } - mainObjStartLoc.add(locationNumberById.get(new Pair<>(op.getStartLocation().getId(), isMoored))); - isMovingOp.add((op instanceof MovingTemplate) || (op instanceof MooringTemplate)); - } - writeArray(writer, "main_obj_start_loc", mainObjStartLoc, (Integer val) -> val + 1); - writeArray(writer, "is_moving_operation", isMovingOp, Optional.of(false)); + writeArray(writer, "operations_that_used_obj_as_resource", res, ConversionUtil::setToString, + Optional.of(new TreeSet<>())); writer.write("\n"); } { // counters, operations_resources_start_loc @@ -1274,10 +1395,29 @@ public class ConversionUtil { writeArray(writer, "operation_of_counter", operationOfCounter, (Integer val) -> val + 1); writeArray(writer, "required_counter_values", requiredCounterValues); writeArray(writer, "operations_resources_start_loc", operationsResourcesStartLoc, (Integer val) -> val + 1); - writeArray(writer, "counters_of_operation", countersOfOperation, ConversionUtil::setToString); + writeArray(writer, "counters_of_operation", countersOfOperation, ConversionUtil::setToString, + Optional.of(new TreeSet<>())); } } + void requiredLocationsOnOpStart() throws IOException { + ArrayList mainObjStartLoc = new ArrayList<>(); + ArrayList bunkerStartLoc = new ArrayList<>(); + for (OperationTemplate op : operationTemplates) { + boolean isMoored = false; + if ((op instanceof LoadingTemplate) && (((LoadingTemplate) op).getWithMooring())) { + isMoored = true; + } + if ((op instanceof MooringTemplate) && (!((MooringTemplate)op).isDirect())) { + isMoored = true; + } + mainObjStartLoc.add(getLocNById(op.getStartLocation().getId(), isMoored)); + bunkerStartLoc.add(getLocNById(op.getStartLocation().getId(), false)); + } + writeArray(writer, "main_obj_start_loc", mainObjStartLoc, (Integer val) -> val + 1, Optional.of(-1)); + writeArray(writer, "bunker_start_loc", bunkerStartLoc, (Integer val) -> val + 1, Optional.of(-1)); + } + /* С типизацией. */ void portToMiniZinc_2() throws IOException { if (!task.isTypified()) { @@ -1296,15 +1436,19 @@ public class ConversionUtil { movingObjectLocationDefinition(false); initialLocations(); finalLocations(); + + defDataForCurMovingOp(); + weatherWindowsNewFormat(); conflictingOperationsOnStorageAndOnTypeOnMainObject(); + requiredLocationsOnOpStart(); + operationsContinuity(); typifiedResourcesDefinition(); - unmooredCargoOp(); - - cargoOpUsingObjAsMain(); + cargoOpUsingObj(); + addOpWithNominallyMooring(); writer.write("n_obj_with_storage = " + nObjWithStorage + ";\n"); writer.write("n_cargo_types = " + cargoes.size() + ";\n"); @@ -1346,7 +1490,7 @@ public class ConversionUtil { } } - public static void resolveMiniZincResults(TaskCase task, String fileName) { + public static void resolveMiniZincResults(TaskCase taskCase, String fileName) { ArrayList operations = null; Integer result = null; @@ -1386,7 +1530,7 @@ public class ConversionUtil { while ((pos < line.length()) && (line.charAt(pos) != '[') && (line.charAt(pos) != '{')) { pos++; } - int arrayFirstDim = ((int) task.getPlanningInterval()) + 2; + int arrayFirstDim = ((int) taskCase.getPlanningInterval()) + 2; if (line.charAt(pos) == '{') { pos++; @@ -1429,78 +1573,87 @@ public class ConversionUtil { if (result == null) { throw new ParserException("No result in input"); } - if (!arrays.containsKey("op_status")) { - if (result == -1) { - operations = new ArrayList<>(); - } else { - throw new ParserException("No \"op_status\" in input"); + + for (String keyArray : Arrays.asList("op_status", "participation_as_resource")) { + if (! arrays.containsKey(keyArray)) { + if (result == -1) { + operations = new ArrayList<>(); + } else { + throw new ParserException("No \"" + keyArray + "\" in input"); + } } } + if (result != -1) { + Task task = new Task(taskCase, ""); + operations = new ArrayList<>(); ArrayList> opStatus = arrays.get("op_status"); - ArrayList templates = new ArrayList<>(task.getTemplates()); - if (task.isTypified()) { - templates = Task.renumberOperations(task); + ArrayList templates = new ArrayList<>(taskCase.getTemplates()); + if (taskCase.isTypified()) { + templates = Task.renumberOperations(taskCase); } Map operationById = new TreeMap<>(); - for (OperationTemplate operation : task.getTemplates()) { + for (OperationTemplate operation : taskCase.getTemplates()) { operationById.put(operation.getId(), operation); } Map objByNo = new TreeMap<>(); ArrayList> isFixed; - { - Task t = new Task(task, ""); - ArrayList movingObjects = new ArrayList<>(); - movingObjects.addAll(task.getShips()); - movingObjects.addAll(task.getTows()); - movingObjects.addAll(task.getEquipments()); - - for (MovingObject obj : movingObjects) { - objByNo.put(t.getMObjNumberById().get(obj.getId()), obj); - } - isFixed = t.getIsFixedArray(); + for (MovingObject obj : task.getMovingObjects()) { + objByNo.put(task.getMObjNumberById().get(obj.getId()), obj); } + isFixed = task.getIsFixedArray(); + Set oldSolution = new TreeSet<>(); - for (Operation op : task.getSolution()) { + for (Operation op : taskCase.getSolution()) { if (op.getFixation()) { oldSolution.add(op.toString()); } } - for (int opNo = 0; opNo < opStatus.size(); opNo++) { + for (int opNo = 1; opNo < opStatus.size(); opNo++) { int duration = 0; int t = 0; boolean lastFixation = false; while (t < opStatus.get(opNo).size()) { - if ((opStatus.get(opNo).get(t).equals("true") && (lastFixation != isFixed.get(opNo).get(t - 1))) + if ((opStatus.get(opNo).get(t).equals("true") && (lastFixation != isFixed.get(opNo).get(t)) && (duration != 0)) || (opStatus.get(opNo).get(t).equals("false") && (duration != 0))) { // Добавляем новую операцию. Operation op = new Operation(); op.setStart(t - duration - 1); op.setDuration(duration); - op.setTemplate(operationById.get(templates.get(opNo).getId())); + op.setTemplate(operationById.get(templates.get(opNo - 1).getId())); - if (task.isTypified()) { - op.setExecutor(ConversionUtil.getExecutor(templates.get(opNo))); + if (taskCase.isTypified()) { + op.setExecutor(ConversionUtil.getExecutor(templates.get(opNo - 1))); ArrayList resources = new ArrayList<>(); // TODO ускорить. ArrayList> opOfResource = arrays.get("participation_as_resource"); - for (int obj = 0; obj < opOfResource.size(); obj++) { - if (opOfResource.get(obj).get(t - 1).equals(Integer.toString(opNo + 1))) { - resources.add(objByNo.get(obj)); + for (int obj = 1; obj < opOfResource.size(); obj++) { + if (opOfResource.get(obj).get(t - 1).equals(Integer.toString(opNo))) { + resources.add(objByNo.get(obj - 1)); } } op.setResources(resources); } + { + OperationTemplate template = templates.get(opNo - 1); + if (template instanceof LoadingTemplate) { + LoadingTemplate operation = (LoadingTemplate)template; + if (operation.getBunker().isPresent()) { + // TODO Доделать. + } + } + } + op.setFixation(true); if (! oldSolution.contains(op.toString())) { op.setFixation(false); @@ -1509,22 +1662,24 @@ public class ConversionUtil { operations.add(op); duration = 0; } - if (opStatus.get(opNo).get(t).equals("true") && (lastFixation != isFixed.get(opNo).get(t - 1))) { // Остаёмся на месте. - lastFixation = isFixed.get(opNo).get(t - 1); + if (opStatus.get(opNo).get(t).equals("true") && (lastFixation != isFixed.get(opNo).get(t))) { // Остаёмся на месте. + lastFixation = isFixed.get(opNo).get(t); continue; } if (opStatus.get(opNo).get(t).equals("true")) { duration++; + } else { + duration = 0; } - if ((0 <= t - 1) && (t - 1 < isFixed.get(opNo).size())) { - lastFixation = isFixed.get(opNo).get(t - 1); + if ((0 <= t) && (t < isFixed.get(opNo).size())) { + lastFixation = isFixed.get(opNo).get(t); } t++; } } } - task.setSolution(operations); - task.setSolution_result(result); + taskCase.setSolution(operations); + taskCase.setSolution_result(result); } catch (IOException e) { throw new UncheckedIOException(e); } diff --git a/src/inport/LoadingTemplate.java b/src/inport/LoadingTemplate.java index 1196afd..ec1bdbc 100644 --- a/src/inport/LoadingTemplate.java +++ b/src/inport/LoadingTemplate.java @@ -6,6 +6,7 @@ package inport; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.OptionalInt; /** @@ -15,7 +16,9 @@ import java.util.OptionalInt; public class LoadingTemplate extends OperationTemplate { private TransportShip loader; - private OptionalInt loaderType = OptionalInt.empty(); + private OptionalInt loaderType = OptionalInt.empty(); + private Optional bunker = Optional.empty(); + private OptionalInt bunkerType = OptionalInt.empty(); private Storage storage; private List resources; private List resourcesTypes; @@ -23,6 +26,20 @@ public class LoadingTemplate extends OperationTemplate { private boolean withMooring; private Cargo cargo; + public OptionalInt getBunkerType() { + return bunkerType; + } + public void setBunkerType(OptionalInt bunkerType) { + this.bunkerType = bunkerType; + } + + public Optional getBunker() { + return bunker; + } + public void setBunker(Optional bunker) { + this.bunker = bunker; + } + public OptionalInt getLoaderType() { return loaderType; } @@ -95,11 +112,19 @@ public class LoadingTemplate extends OperationTemplate { return cargo; } - public LoadingTemplate(TransportShip loader, Berth berth, Storage storage, Cargo cargo, - List resourcesTypes, boolean withMooring, double intensity, int id) { + public LoadingTemplate(TransportShip loader, + Berth berth, + Storage storage, + Cargo cargo, + Optional bunker, + List resourcesTypes, + boolean withMooring, + double intensity, + int id) { super(id, berth); this.loader = loader; this.storage = storage; + this.bunker = bunker; this.resources = new ArrayList<>(); this.resourcesTypes = new ArrayList<>(resourcesTypes); this.withMooring = withMooring; @@ -130,20 +155,20 @@ public class LoadingTemplate extends OperationTemplate { res += t; first = false; } - int startId; - if (loaderType.isPresent()) { - startId = loaderType.getAsInt(); - } else { - startId = loader.getId(); - } - int source = startId; - if (intensity>0) - source = storage.getId(); - int target = startId; - if (intensity<=0) - target = storage.getId(); + int source = (loaderType.isPresent() ? loaderType.getAsInt() : loader.getId()); + int target; + if (bunkerType.isPresent()) { + target = bunkerType.getAsInt(); + } else target = bunker.map(MovingObject::getId).orElseGet(() -> storage.getId()); + + if (intensity > 0) { + int temp = source; + source = target; + target = temp; + } return getId() + "; " + "loa; " + twtoString() + "; " + source + "; " + cargo.getId() + "; " + target + "; " - + getStartLocation().getId() + "; [" + res +"]; " + Math.abs(intensity) + "; " + (withMooring ? "M" : "U"); - } + + getStartLocation().getId() + "; [" + res +"]; " + Math.abs(intensity) + "; " + + (withMooring ? "M" : "U"); + } } diff --git a/src/inport/Main.java b/src/inport/Main.java index 46a6c26..bb59bf2 100644 --- a/src/inport/Main.java +++ b/src/inport/Main.java @@ -100,7 +100,6 @@ public class Main { System.out.println(e.getMessage()); break; } - String error = solveTask_2(task); if (!error.isEmpty()) { @@ -148,10 +147,7 @@ public class Main { } Task t = new Task(task, ""); { - ArrayList movingObjects = new ArrayList<>(); - movingObjects.addAll(task.getShips()); - movingObjects.addAll(task.getTows()); - movingObjects.addAll(task.getEquipments()); + ArrayList movingObjects = Task.calcMovingObjects(task); Map objByNo = new TreeMap<>(); for (MovingObject obj : movingObjects) { diff --git a/src/inport/Operation.java b/src/inport/Operation.java index 32cf9a6..c04c7f4 100644 --- a/src/inport/Operation.java +++ b/src/inport/Operation.java @@ -5,6 +5,7 @@ package inport; import java.util.List; +import java.util.Optional; /** * @@ -17,8 +18,16 @@ public class Operation { private double start; private double duration; private MovingObject executor; + private Optional bunker = Optional.empty(); private List resources; + public Optional getBunker() { + return bunker; + } + public void setBunker(Optional bunker) { + this.bunker = bunker; + } + public boolean getFixation() { return fixation; } @@ -110,7 +119,8 @@ public class Operation { StringBuilder sb = new StringBuilder(); sb.append(template.getId()).append("; "); sb.append(fixation ? "F" : "R").append("; ").append(start).append("; ").append(duration); - sb.append(" (").append(executor.getId()).append(" ["); + sb.append(" (").append(executor.getId()).append(" "); + sb.append(bunker.map(b -> b.getId() + " ").orElse("")).append("["); boolean isFirst = true; for (MovingObject obj : resources) { if (isFirst) { diff --git a/src/inport/Storage.java b/src/inport/Storage.java index 134139e..20b3e28 100644 --- a/src/inport/Storage.java +++ b/src/inport/Storage.java @@ -101,7 +101,7 @@ public class Storage { @Override public String toString() { - return id + "; " + name + "; " + cargo.getId() + "; " +volume; + return id + "; " + name + "; " + cargo.getId() + "; " + volume; } public Storage(String s, Map cargoes) { diff --git a/src/inport/StorageState.java b/src/inport/StorageState.java index 6a9d57c..f05b8cb 100644 --- a/src/inport/StorageState.java +++ b/src/inport/StorageState.java @@ -82,15 +82,24 @@ public class StorageState { return ""; } - public StorageState(String s, Map mp, Map vp, Map cp) { + public StorageState(String s, + Map mp, + Map vp, + Map bunkers, + Map cp) { String[] tokens = s.split(";"); int key = Integer.parseInt(tokens[0].trim()); cargo = cp.get(key); key = Integer.parseInt(tokens[1].trim()); - if (mp.containsKey(key)) + if (mp.containsKey(key)) { storage = mp.get(key); - if (vp.containsKey(key)) + } + if (vp.containsKey(key)) { storage = vp.get(key); + } + if (bunkers.containsKey(key)) { + storage = bunkers.get(key); + } cargoState = Double.parseDouble(tokens[2].trim()); } diff --git a/src/inport/TaskCase.java b/src/inport/TaskCase.java index b2a0a53..06c6cf4 100644 --- a/src/inport/TaskCase.java +++ b/src/inport/TaskCase.java @@ -35,6 +35,9 @@ public class TaskCase { // Обслуживаемые суда private List ships; + // Типы бункеровщиков. + private Map bunkerTypes; + // Типы обслуживаемых судов private Map vesselTypes; @@ -67,6 +70,13 @@ public class TaskCase { // План - решение private List solution; + public Map getBunkerTypes() { + return bunkerTypes; + } + public void setBunkerTypes(Map bunkerTypes) { + this.bunkerTypes = bunkerTypes; + } + /** * Get the value of solution * @@ -385,6 +395,7 @@ public class TaskCase { equipments = new ArrayList<>(); ships = new ArrayList<>(); + bunkerTypes = new TreeMap<>(); vesselTypes = new TreeMap<>(); equipmentTypes = new TreeMap<>(); @@ -401,12 +412,15 @@ public class TaskCase { solution = new ArrayList<>(); } - private MovingObjectState fromString(String s, Map m_berth, Map m_vessel) + private MovingObjectState fromString(String s, + Map m_berth, + Map m_vessel, + Map m_bunker) { String[] tkns1 = s.split(";"); MovingObjectState st = new MovingObjectState(); int key = Integer.parseInt(tkns1[0].trim()); - MovingObject vs = m_vessel.get(key); + MovingObject vs = m_vessel.containsKey(key) ? m_vessel.get(key) : m_bunker.get(key); st.setVessel(vs); key = Integer.parseInt(tkns1[1].trim()); st.setLocation(m_berth.get(key)); @@ -422,6 +436,7 @@ public class TaskCase { Loading_Equipment_Types("Loading Equipment Types"), Vessel_Types ("Vessel Types"), Bunkers ("Bunkers"), + Bunker_Types ("Bunker Types"), Tows ("Tows"), Loading_Equipments ("Loading Equipments"), Transport_Ships ("Transport Ships"), @@ -453,6 +468,7 @@ public class TaskCase { { cargoes.clear(); berths.clear(); storages.clear(); bunkers.clear(); tows.clear(); equipments.clear(); ships.clear(); templates.clear(); cargoFlows.clear(); vesselInitialState.clear(); storageInitialState.clear(); vesselEndState.clear(); storageEndState.clear(); + bunkerTypes.clear(); solution.clear(); // Open the file @@ -467,6 +483,7 @@ public class TaskCase { Map m_berth = new HashMap<>(); Map m_storage = new HashMap<>(); Map m_vessel = new HashMap<>(); + Map m_bunker = new HashMap<>(); Map m_equipment = new HashMap<>(); Map m_template = new HashMap<>(); //Read File Line By Line @@ -494,7 +511,8 @@ public class TaskCase { break; case Berths: Berth b = new Berth(strLine); berths.add(b); m_berth.put(b.getId(), b); break; - case Storages: Storage s = new Storage(strLine, m_cargo); storages.add(s); m_storage.put(s.getId(), s); + case Storages: + Storage s = new Storage(strLine, m_cargo); storages.add(s); m_storage.put(s.getId(), s); break; case Vessel_Types: tokens = strLine.split(";"); @@ -505,6 +523,13 @@ public class TaskCase { equipmentTypes.put(Integer.parseInt(tokens[0].trim()), tokens[1].trim()); break; case Bunkers: + Bunker bunker = new Bunker(strLine); + bunkers.add(bunker); + m_bunker.put(bunker.getId(), bunker); + break; + case Bunker_Types : + tokens = strLine.split(";"); + bunkerTypes.put(Integer.parseInt(tokens[0].trim()), tokens[1].trim()); break; case Tows: Tow t = new Tow(strLine); tows.add(t); m_vessel.put(t.getId(), t); break; @@ -577,7 +602,8 @@ public class TaskCase { LoadingTemplate mt = new LoadingTemplate(); mt.setId(Integer.parseInt(tokens[0].trim())); mt.setTimeWindow(tokens[2].trim()); - int direct = 1; + + int direction = 1; BiFunction addLoaderOrStorage = (Integer key, Integer loaderDirection) -> { int dir = 1; @@ -588,17 +614,27 @@ public class TaskCase { if (m_vessel.containsKey(key)) { mt.setLoader((TransportShip)m_vessel.get(key)); dir = loaderDirection; - } + } else if (m_storage.containsKey(key)) { - mt.setStorage((Storage)m_storage.get(key)); + mt.setStorage(m_storage.get(key)); + dir = -loaderDirection; + } else + if (m_bunker.containsKey(key)) { + mt.setBunker(Optional.of(m_bunker.get(key))); + dir = -loaderDirection; + } else + if (bunkerTypes.containsKey(key)) { + mt.setBunkerType(OptionalInt.of(key)); dir = -loaderDirection; } return dir; }; - // Источник. Пока пара - это только хранилище-судно. С бункеровкой будем разбираться потом - int key = Integer.parseInt(tokens[3].trim()); - direct = addLoaderOrStorage.apply(key, -1); + int key; + + // Источник. + key = Integer.parseInt(tokens[3].trim()); + direction = addLoaderOrStorage.apply(key, -1); // Груз. key = Integer.parseInt(tokens[4].trim());; for (Cargo cargo : cargoes) { @@ -606,9 +642,9 @@ public class TaskCase { mt.setCargo(cargo); } } - // Приемник. Пока пара - это только хранилище-судно. С бункеровкой будем разбираться потом + // Приемник. key = Integer.parseInt(tokens[5].trim()); - direct = addLoaderOrStorage.apply(key, 1); + direction = addLoaderOrStorage.apply(key, 1); key = Integer.parseInt(tokens[6].trim()); mt.setStartLocation(m_berth.get(key)); String[] rs = tokens[7].trim().replace("[", "").replace("]", "").split(","); @@ -621,7 +657,7 @@ public class TaskCase { mt.getResources().add(m_equipment.get(key)); } } - mt.setIntensity(direct*Double.parseDouble(tokens[8].trim())); + mt.setIntensity(direction*Double.parseDouble(tokens[8].trim())); mt.setWithMooring(tokens[9].trim().equals("M")); templates.add(mt); @@ -632,13 +668,13 @@ public class TaskCase { break; case Cargo_Flows: cargoFlows.add(new CargoFlow(strLine, m_storage, m_cargo)); break; - case Initial_Vessel_State: vesselInitialState.add(fromString(strLine, m_berth, m_vessel)); + case Initial_Vessel_State: vesselInitialState.add(fromString(strLine, m_berth, m_vessel, m_bunker)); break; - case Initial_Storage_State: storageInitialState.add(new StorageState(strLine, m_storage, m_vessel, m_cargo)); + case Initial_Storage_State: storageInitialState.add(new StorageState(strLine, m_storage, m_vessel, m_bunker, m_cargo)); break; - case Final_Vessel_State: vesselEndState.add(fromString(strLine, m_berth, m_vessel)); + case Final_Vessel_State: vesselEndState.add(fromString(strLine, m_berth, m_vessel, m_bunker)); break; - case Final_Storage_State: storageEndState.add(new StorageState(strLine, m_storage, m_vessel, m_cargo)); + case Final_Storage_State: storageEndState.add(new StorageState(strLine, m_storage, m_vessel, m_bunker, m_cargo)); break; case Task_Properties: { @@ -719,7 +755,13 @@ public class TaskCase { for (Map.Entry e : vesselTypes.entrySet()) { writer.write(e.getKey() + "; " + e.getValue() + "\n"); } + + writer.write("\n" + Tag.Bunker_Types.text + "\n"); + for (Map.Entry e : bunkerTypes.entrySet()) { + writer.write(e.getKey() + "; " + e.getValue() + "\n"); + } } + writer.write("\nBunkers"+"\n"); for (Bunker c : bunkers) writer.write(c.toString()+"\n"); diff --git a/tests/with_typing/Bunkers.tipp b/tests/with_typing/Bunkers.tipp new file mode 100644 index 0000000..6df16f1 --- /dev/null +++ b/tests/with_typing/Bunkers.tipp @@ -0,0 +1,94 @@ +Typified +1 + +Cargoes +10001; Нечто; 0.0 +10002; Топливо; 0.0 + +Berths +1; Raid +2; Pier 1 +3; Pier 2 + +Storages +4; Storage 1; 10001; 10000.0 + +Vessel Types +1001; Тип судна 1 + +Bunker Types +2001; Тип бункировщика 1 + +Bunkers +201; Bunker 1; 2000.0; 2001 +202; Bunker 2; 2000.0; 2001 + +Tows + +Loading Equipment Types + +Loading Equipments + +Transport Ships +101; Ship 1; 2000.0; 1001 +102; Ship 2; 2000.0; 1001 + +Templates +7; mov; []; 1001; 1; 2; []; 1.0 +8; mov; []; 1001; 2; 1; []; 1.0 +9; mov; []; 1001; 1; 3; []; 1.0 +10; mov; []; 1001; 3; 1; []; 1.0 +11; mov; []; 1001; 2; 3; []; 1.0 +12; mov; []; 1001; 3; 2; []; 1.0 +13; mov; []; 2001; 3; 2; []; 1.0 +14; mov; []; 2001; 3; 2; []; 1.0 +19; loa; []; 4; 10001; 1001; 2; []; 100.0; U +20; loa; []; 2001; 10002; 1001; 2; []; 10.0; U + +Cargo Flows + +Initial Vessel State +101; 1 +102; 1 +201; 2 +202; 2 + +Initial Storage State +10001; 101; 0.0 +10002; 101; 0.0 +10001; 102; 0.0 +10002; 102; 0.0 +10002; 201; 20.0 +10002; 202; 20.0 +10001; 4; 10000.0 + +Final Vessel State +101; 1 +102; 1 + +Final Storage State +10001; 101; 300.0 +10002; 101; 20.0 +10001; 102; 300.0 +10002; 102; 20.0 + + +Task Properties +30.0; 0 + + +Solution +8.0 +7; R; 0.0; 1.0 (101 []) +9; R; 0.0; 1.0 (102 []) +19; R; 1.0; 3.0 (101 []) +20; R; 1.0; 1.0 (101 []) +10; R; 2.0; 1.0 (102 []) +7; R; 3.0; 1.0 (102 []) +20; R; 3.0; 1.0 (101 []) +11; R; 4.0; 1.0 (101 []) +19; R; 4.0; 3.0 (102 []) +20; R; 4.0; 1.0 (102 []) +20; R; 5.0; 1.0 (102 []) +8; R; 7.0; 1.0 (102 []) +10; R; 7.0; 1.0 (101 []) diff --git a/tests/with_typing/v2.tipp b/tests/with_typing/v2.tipp index 8e0c354..10f7ca4 100644 --- a/tests/with_typing/v2.tipp +++ b/tests/with_typing/v2.tipp @@ -69,7 +69,7 @@ Final Storage State Task Properties -30.0; 0 +22.0; 0 Solution diff --git "a/\320\237\320\273\320\260\320\275\320\270\321\200\320\276\320\262\320\260\320\275\320\270\320\265 \320\277\320\276\321\200\321\202\320\276\320\262\321\213\321\205 \320\276\320\277\320\265\321\200\320\260\321\206\320\270\320\271.docx" "b/\320\237\320\273\320\260\320\275\320\270\321\200\320\276\320\262\320\260\320\275\320\270\320\265 \320\277\320\276\321\200\321\202\320\276\320\262\321\213\321\205 \320\276\320\277\320\265\321\200\320\260\321\206\320\270\320\271.docx" index 23d4ad6746c292f13c5853d2c7338507a367facd..13dac3982343770318491436e05bb0b0ee136095 100644 GIT binary patch delta 36621 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 delta 36064 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
xlhX`B0tw@-x<2V4%=Ry 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 -- GitLab