Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve computation of vehicle loading times #940

Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ export class SimulatedRegionOverviewBehaviorTransferVehiclesComponent
).length,
remainingTime:
activeActivityState.startTime +
activeActivityState.loadDelay -
(activeActivityState.loadDelay ?? 0) -
currentTime,
}))
);
Expand Down
67 changes: 54 additions & 13 deletions shared/src/simulation/activities/load-vehicle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
IsUUID,
Min,
} from 'class-validator';
import { difference } from 'lodash-es';
import {
SimulatedRegionPosition,
VehiclePosition,
Expand Down Expand Up @@ -61,7 +62,16 @@ export class LoadVehicleActivityState implements SimulationActivityState {

@IsInt()
@Min(0)
public readonly loadDelay: number;
@IsOptional()
public readonly loadDelay?: number = undefined;

@IsInt()
@Min(0)
public readonly loadTimePerPatient: number;

@IsInt()
@Min(0)
public readonly personnelLoadTime: number;

@IsOptional()
@IsString()
Expand All @@ -83,15 +93,17 @@ export class LoadVehicleActivityState implements SimulationActivityState {
transferDestinationType: TransferDestination,
transferDestinationId: UUID,
patientsToBeLoaded: UUIDSet,
loadDelay: number,
loadTimePerPatient: number,
personnelLoadTime: number,
key?: string
) {
this.id = id;
this.vehicleId = vehicleId;
this.transferDestinationType = transferDestinationType;
this.transferDestinationId = transferDestinationId;
this.patientsToBeLoaded = patientsToBeLoaded;
this.loadDelay = loadDelay;
this.loadTimePerPatient = loadTimePerPatient;
this.personnelLoadTime = personnelLoadTime;
this.key = key;
}

Expand Down Expand Up @@ -119,6 +131,8 @@ export const loadVehicleActivity: SimulationActivity<LoadVehicleActivityState> =
if (!activityState.hasBeenStarted) {
// Send remove events

let personnelToLoadCount = 0;

Object.keys(vehicle.personnelIds).forEach((personnelId) => {
const personnel = getElement(
draftState,
Expand All @@ -135,6 +149,7 @@ export const loadVehicleActivity: SimulationActivity<LoadVehicleActivityState> =
simulatedRegion,
PersonnelRemovedEvent.create(personnelId)
);
personnelToLoadCount++;
}
});
Object.keys(vehicle.materialIds).forEach((materialId) => {
Expand Down Expand Up @@ -182,28 +197,35 @@ export const loadVehicleActivity: SimulationActivity<LoadVehicleActivityState> =

// Load patients (and unload patients not to be loaded)

Object.keys(vehicle.patientIds).forEach((patientId) => {
const patientsToUnload = difference(
Object.keys(vehicle.patientIds),
Object.keys(activityState.patientsToBeLoaded)
);
const patientsToLoad = difference(
Object.keys(activityState.patientsToBeLoaded),
Object.keys(vehicle.patientIds)
);

patientsToUnload.forEach((patientId) => {
changePositionWithId(
patientId,
SimulatedRegionPosition.create(simulatedRegion.id),
'patient',
draftState
);

// Inform the region that a new patient has left the vehicle
// (Only if it actually left the vehicle and will not be instantly re-added)
if (!activityState.patientsToBeLoaded[patientId]) {
sendSimulationEvent(
simulatedRegion,
NewPatientEvent.create(patientId)
);
}
sendSimulationEvent(
simulatedRegion,
NewPatientEvent.create(patientId)
);
});

vehicle.patientIds = cloneDeepMutable(
activityState.patientsToBeLoaded
);

Object.keys(vehicle.patientIds).forEach((patientId) => {
patientsToLoad.forEach((patientId) => {
changePositionWithId(
patientId,
VehiclePosition.create(vehicle.id),
Expand All @@ -212,13 +234,32 @@ export const loadVehicleActivity: SimulationActivity<LoadVehicleActivityState> =
);
});

const patientMovementsCount =
patientsToUnload.length + patientsToLoad.length;

// Personnel has to leave and reenter the vehicle if patients are unloaded or loaded
const personnelLoadingRequired =
personnelToLoadCount > 0 || patientMovementsCount > 0;

// Calculate loading time based on the patients and personnel to be loaded
// Do not do the calculation if the time is already set (which could occur if an instance of this activity was imported from an older state version)
if (activityState.loadDelay === undefined) {
activityState.loadDelay =
patientMovementsCount *
activityState.loadTimePerPatient +
(personnelLoadingRequired
? activityState.personnelLoadTime
: 0);
}

activityState.hasBeenStarted = true;
activityState.startTime = draftState.currentTime;
}

if (
activityState.loadDelay !== undefined &&
activityState.startTime + activityState.loadDelay <=
draftState.currentTime
draftState.currentTime
) {
// terminate if the occupation has changed
if (
Expand Down
16 changes: 6 additions & 10 deletions shared/src/simulation/behaviors/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,8 @@ export const transferBehavior: SimulationBehavior<TransferBehaviorState> = {
event.transferDestinationType,
event.transferDestinationId,
event.patientIds,
Math.max(
Object.keys(event.patientIds).length *
behaviorState.loadTimePerPatient,
behaviorState.personnelLoadTime
)
behaviorState.loadTimePerPatient,
behaviorState.personnelLoadTime
)
);
vehicleToLoad.occupation = cloneDeepMutable(
Expand Down Expand Up @@ -175,11 +172,8 @@ export const transferBehavior: SimulationBehavior<TransferBehaviorState> = {
event.transferDestinationType,
event.transferDestinationId,
event.patientIds,
Math.max(
Object.keys(event.patientIds).length *
behaviorState.loadTimePerPatient,
behaviorState.personnelLoadTime
)
behaviorState.loadTimePerPatient,
behaviorState.personnelLoadTime
)
);
vehicle.occupation = cloneDeepMutable(
Expand Down Expand Up @@ -208,6 +202,7 @@ export const transferBehavior: SimulationBehavior<TransferBehaviorState> = {
event.transferDestinationType,
event.transferDestinationId,
{},
behaviorState.loadTimePerPatient,
behaviorState.personnelLoadTime
)
);
Expand Down Expand Up @@ -283,6 +278,7 @@ export const transferBehavior: SimulationBehavior<TransferBehaviorState> = {
event.transferDestinationType,
event.transferDestinationId,
{},
behaviorState.loadTimePerPatient,
behaviorState.personnelLoadTime
)
);
Expand Down
46 changes: 46 additions & 0 deletions shared/src/state-migrations/31-improve-load-vehicle-activity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { Migration } from './migration-functions';

interface SimulatedRegionStub {
activities: { [key: string]: ActivityStateStub };
}

interface LoadVehicleActivityStateStub {
type: 'loadVehicleActivity';
loadTimePerPatient: number;
personnelLoadTime: number;
}

type ActivityStateStub =
| LoadVehicleActivityStateStub
| {
type: Exclude<'loadVehicleActivity', unknown>;
};

export const improveLoadVehicleActivity31: Migration = {
// Only the data model of an activity has been changed, so there are no actions that have to be migrated
action: null,

state: (state) => {
const typedState = state as {
simulatedRegions: {
[key: string]: SimulatedRegionStub;
};
};

Object.values(typedState.simulatedRegions).forEach(
(simulatedRegion) => {
Object.values(simulatedRegion.activities).forEach(
(activityState) => {
if (activityState.type === 'loadVehicleActivity') {
// The loadTimePerPatient and personnelLoadTime must be set for the validation to pass
// However, the activity will still use the old loadDelay if it is set
// Therefore, it is okay to set the new variables to 0
activityState.loadTimePerPatient = 0;
activityState.personnelLoadTime = 0;
}
}
);
}
);
},
};
2 changes: 2 additions & 0 deletions shared/src/state-migrations/migration-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { activitiesToUnloadVehiclesBehavior28 } from './28-add-activities-to-unl
import { removeTransferVehiclesActivityAndChangeAnswerRequestBehavior29 } from './29-remove-transfer-vehicles-activity-and-change-answer-request-behavior';
import { updateEocLog3 } from './3-update-eoc-log';
import { reportTreatmentStatusChanges30 } from './30-report-treatment-status-changes';
import { improveLoadVehicleActivity31 } from './31-improve-load-vehicle-activity';
import { removeSetParticipantIdAction4 } from './4-remove-set-participant-id-action';
import { removeStatistics5 } from './5-remove-statistics';
import { removeStateHistory6 } from './6-remove-state-history';
Expand Down Expand Up @@ -83,4 +84,5 @@ export const migrations: {
28: activitiesToUnloadVehiclesBehavior28,
29: removeTransferVehiclesActivityAndChangeAnswerRequestBehavior29,
30: reportTreatmentStatusChanges30,
31: improveLoadVehicleActivity31,
};
2 changes: 1 addition & 1 deletion shared/src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,5 +160,5 @@ export class ExerciseState {
*
* This number MUST be increased every time a change to any object (that is part of the state or the state itself) is made in a way that there may be states valid before that are no longer valid.
*/
static readonly currentStateVersion = 30;
static readonly currentStateVersion = 31;
}