Feat : Prio order for auto stream record
This commit is contained in:
@@ -884,10 +884,28 @@ export interface WatchTarget {
|
||||
favorite: boolean;
|
||||
/**
|
||||
* Lancer l'enregistrement sans intervention dès que ce profil est en direct
|
||||
* depuis assez longtemps. Exige un agent assigné, et un seul profil en
|
||||
* automatisme par agent : une VM n'enregistre qu'un flux à la fois.
|
||||
* depuis assez longtemps. Exige un agent assigné.
|
||||
*
|
||||
* Plusieurs profils peuvent l'activer sur une même VM : elle n'en capture
|
||||
* qu'un à la fois, et c'est {@link WatchTarget.priority} qui arbitre.
|
||||
*/
|
||||
autoRecord: boolean;
|
||||
/**
|
||||
* Rang dans la file d'attente d'une VM, le plus élevé l'emportant.
|
||||
*
|
||||
* L'échelle est globale pour rester comparable partout, mais elle ne tranche
|
||||
* qu'entre profils assignés au même agent : deux VM distinctes ne se
|
||||
* disputent rien.
|
||||
*/
|
||||
priority: number;
|
||||
/**
|
||||
* Autorise ce profil à couper une capture de priorité inférieure sur sa VM.
|
||||
*
|
||||
* Séparé de la priorité, et non déduit d'elle : interrompre un
|
||||
* enregistrement en cours perd la fin d'un fichier. Cela ne doit pas
|
||||
* découler d'un simple changement de rang, mais d'un choix explicite.
|
||||
*/
|
||||
preempt: boolean;
|
||||
state: StreamState;
|
||||
rawStatus: string | null;
|
||||
/** Depuis quand l'état est stable, selon nos propres observations. */
|
||||
@@ -963,6 +981,76 @@ export function recordingUsername(agent: Pick<AgentView, 'status' | 'watch'>): s
|
||||
return agent.watch.username || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rangs proposés par l'interface. La colonne reste un entier libre : affiner
|
||||
* l'échelle un jour ne demandera pas de migration.
|
||||
*/
|
||||
export const PRIORITY_LEVELS = [
|
||||
{ value: 0, label: 'Basse' },
|
||||
{ value: 1, label: 'Normale' },
|
||||
{ value: 2, label: 'Haute' },
|
||||
{ value: 3, label: 'Critique' },
|
||||
] as const;
|
||||
|
||||
export const DEFAULT_PRIORITY = 1;
|
||||
|
||||
export function priorityLabel(value: number): string {
|
||||
return PRIORITY_LEVELS.find((level) => level.value === value)?.label ?? `Rang ${value}`;
|
||||
}
|
||||
|
||||
export interface PreemptionVerdict {
|
||||
allowed: boolean;
|
||||
/** Pourquoi l'interruption est refusée — destiné à être affiché tel quel. */
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* `candidate` a-t-il le droit de couper la capture en cours au profit de la sienne ?
|
||||
*
|
||||
* Trois refus, dans cet ordre, du plus prudent au plus attendu :
|
||||
*
|
||||
* 1. **Capture non identifiée.** Si l'on ne sait pas quel profil la VM
|
||||
* enregistre, on ne la coupe pas : le risque est de détruire une capture
|
||||
* dont on ignore la valeur, et l'on n'a de toute façon aucune priorité à
|
||||
* lui comparer.
|
||||
* 2. **Interruption non autorisée** sur le profil candidat.
|
||||
* 3. **Priorité insuffisante** — strictement supérieure exigée, ce qui rend
|
||||
* les égalités inoffensives : à rang égal, la capture en place l'emporte.
|
||||
*/
|
||||
export function preemptionVerdict(
|
||||
candidate: Pick<WatchTarget, 'id' | 'priority' | 'preempt'>,
|
||||
current: Pick<WatchTarget, 'id' | 'username' | 'label' | 'priority'> | null,
|
||||
): PreemptionVerdict {
|
||||
if (!current) {
|
||||
return {
|
||||
allowed: false,
|
||||
reason: "la capture en cours n'a pas pu être rattachée à un profil suivi",
|
||||
};
|
||||
}
|
||||
// Rien à interrompre : c'est déjà ce profil qui est capturé. Refusé plutôt
|
||||
// qu'autorisé — l'appelant s'apprêtait à lancer une seconde capture.
|
||||
if (current.id === candidate.id) {
|
||||
return { allowed: false, reason: 'sa capture est déjà en cours sur cette VM' };
|
||||
}
|
||||
|
||||
const name = current.label ?? current.username;
|
||||
if (!candidate.preempt) {
|
||||
return {
|
||||
allowed: false,
|
||||
reason: `« ${name} » est en cours de capture et l'interruption n'est pas autorisée sur ce profil`,
|
||||
};
|
||||
}
|
||||
if (candidate.priority <= current.priority) {
|
||||
return {
|
||||
allowed: false,
|
||||
reason:
|
||||
`« ${name} » est en cours de capture avec une priorité au moins égale ` +
|
||||
`(${priorityLabel(current.priority)})`,
|
||||
};
|
||||
}
|
||||
return { allowed: true };
|
||||
}
|
||||
|
||||
/** Cette VM enregistre-t-elle bien *ce* profil, et non un autre qui lui est assigné ? */
|
||||
export function isRecordingTarget(
|
||||
agent: Pick<AgentView, 'status' | 'watch'> | null | undefined,
|
||||
|
||||
Reference in New Issue
Block a user