Add runtime project scheduler implementation
This commit is contained in:
175
src/runtime-three/runtime-project-scheduler.ts
Normal file
175
src/runtime-three/runtime-project-scheduler.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
import {
|
||||
applyControlEffectToResolvedState,
|
||||
cloneControlEffect,
|
||||
cloneRuntimeResolvedControlState,
|
||||
createActorControlTargetRef,
|
||||
createDefaultResolvedControlSource,
|
||||
createSchedulerResolvedControlSource,
|
||||
createSetActorPresenceControlEffect,
|
||||
type RuntimeResolvedControlState,
|
||||
type SetActorPresenceControlEffect
|
||||
} from "../controls/control-surface";
|
||||
import {
|
||||
cloneProjectScheduler,
|
||||
compareProjectScheduleRoutinePriority,
|
||||
isProjectScheduleRoutineActiveAt,
|
||||
type ProjectScheduler,
|
||||
type ProjectScheduleRoutine
|
||||
} from "../scheduler/project-scheduler";
|
||||
|
||||
export interface RuntimeResolvedActorScheduleState {
|
||||
actorId: string;
|
||||
hasRules: boolean;
|
||||
active: boolean;
|
||||
activeRoutineId: string | null;
|
||||
activeRoutineTitle: string | null;
|
||||
effect: SetActorPresenceControlEffect | null;
|
||||
}
|
||||
|
||||
export interface RuntimeResolvedProjectScheduleState {
|
||||
actors: RuntimeResolvedActorScheduleState[];
|
||||
}
|
||||
|
||||
export interface RuntimeProjectSchedulerState {
|
||||
document: ProjectScheduler;
|
||||
resolved: RuntimeResolvedProjectScheduleState;
|
||||
}
|
||||
|
||||
function cloneRuntimeResolvedActorScheduleState(
|
||||
state: RuntimeResolvedActorScheduleState
|
||||
): RuntimeResolvedActorScheduleState {
|
||||
return {
|
||||
actorId: state.actorId,
|
||||
hasRules: state.hasRules,
|
||||
active: state.active,
|
||||
activeRoutineId: state.activeRoutineId,
|
||||
activeRoutineTitle: state.activeRoutineTitle,
|
||||
effect: state.effect === null ? null : cloneControlEffect(state.effect)
|
||||
};
|
||||
}
|
||||
|
||||
export function cloneRuntimeResolvedProjectScheduleState(
|
||||
state: RuntimeResolvedProjectScheduleState
|
||||
): RuntimeResolvedProjectScheduleState {
|
||||
return {
|
||||
actors: state.actors.map(cloneRuntimeResolvedActorScheduleState)
|
||||
};
|
||||
}
|
||||
|
||||
export function createEmptyRuntimeResolvedProjectScheduleState(): RuntimeResolvedProjectScheduleState {
|
||||
return {
|
||||
actors: []
|
||||
};
|
||||
}
|
||||
|
||||
export function createRuntimeProjectSchedulerState(options: {
|
||||
document: ProjectScheduler;
|
||||
resolved?: RuntimeResolvedProjectScheduleState;
|
||||
}): RuntimeProjectSchedulerState {
|
||||
return {
|
||||
document: cloneProjectScheduler(options.document),
|
||||
resolved: cloneRuntimeResolvedProjectScheduleState(
|
||||
options.resolved ?? createEmptyRuntimeResolvedProjectScheduleState()
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
function resolveActorScheduleRules(
|
||||
scheduler: ProjectScheduler,
|
||||
actorId: string
|
||||
): ProjectScheduleRoutine[] {
|
||||
return Object.values(scheduler.routines)
|
||||
.filter(
|
||||
(routine) =>
|
||||
routine.enabled &&
|
||||
routine.target.kind === "actor" &&
|
||||
routine.target.actorId === actorId &&
|
||||
routine.effect.type === "setActorPresence"
|
||||
)
|
||||
.sort(compareProjectScheduleRoutinePriority);
|
||||
}
|
||||
|
||||
export function resolveRuntimeActorScheduleState(options: {
|
||||
scheduler: ProjectScheduler;
|
||||
actorId: string;
|
||||
dayNumber: number;
|
||||
timeOfDayHours: number;
|
||||
}): RuntimeResolvedActorScheduleState {
|
||||
const actorRules = resolveActorScheduleRules(options.scheduler, options.actorId);
|
||||
const activeRoutine =
|
||||
actorRules.find((routine) =>
|
||||
isProjectScheduleRoutineActiveAt(
|
||||
routine,
|
||||
options.dayNumber,
|
||||
options.timeOfDayHours
|
||||
)
|
||||
) ?? null;
|
||||
|
||||
if (activeRoutine === null) {
|
||||
return {
|
||||
actorId: options.actorId,
|
||||
hasRules: actorRules.length > 0,
|
||||
active: actorRules.length === 0,
|
||||
activeRoutineId: null,
|
||||
activeRoutineTitle: null,
|
||||
effect: null
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
actorId: options.actorId,
|
||||
hasRules: true,
|
||||
active: activeRoutine.effect.active,
|
||||
activeRoutineId: activeRoutine.id,
|
||||
activeRoutineTitle: activeRoutine.title,
|
||||
effect: cloneControlEffect(activeRoutine.effect)
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveRuntimeProjectScheduleState(options: {
|
||||
scheduler: ProjectScheduler;
|
||||
actorIds: string[];
|
||||
dayNumber: number;
|
||||
timeOfDayHours: number;
|
||||
}): RuntimeResolvedProjectScheduleState {
|
||||
const actorIds = [...new Set(options.actorIds)];
|
||||
|
||||
return {
|
||||
actors: actorIds.map((actorId) =>
|
||||
resolveRuntimeActorScheduleState({
|
||||
scheduler: options.scheduler,
|
||||
actorId,
|
||||
dayNumber: options.dayNumber,
|
||||
timeOfDayHours: options.timeOfDayHours
|
||||
})
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
export function applyRuntimeProjectScheduleToControlState(
|
||||
resolved: RuntimeResolvedControlState,
|
||||
schedule: RuntimeResolvedProjectScheduleState
|
||||
): RuntimeResolvedControlState {
|
||||
let nextResolved = cloneRuntimeResolvedControlState(resolved);
|
||||
nextResolved.discrete = nextResolved.discrete.filter(
|
||||
(state) => state.type !== "actorPresence"
|
||||
);
|
||||
|
||||
for (const actorState of schedule.actors) {
|
||||
const effect =
|
||||
actorState.effect ??
|
||||
createSetActorPresenceControlEffect({
|
||||
target: createActorControlTargetRef(actorState.actorId),
|
||||
active: actorState.active
|
||||
});
|
||||
nextResolved = applyControlEffectToResolvedState(
|
||||
nextResolved,
|
||||
effect,
|
||||
actorState.activeRoutineId === null
|
||||
? createDefaultResolvedControlSource()
|
||||
: createSchedulerResolvedControlSource(actorState.activeRoutineId)
|
||||
);
|
||||
}
|
||||
|
||||
return nextResolved;
|
||||
}
|
||||
Reference in New Issue
Block a user