| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558 |
- import 'dart:developer';
- import 'package:collection/collection.dart';
- import 'package:jiffy/jiffy.dart';
- import 'package:tp5/core/core.dart';
- import 'package:tp5/providers/airports.dart';
- import 'package:tp5/roster/models/duty.dart';
- import 'package:timezone/timezone.dart' as tz;
- class Ftl {
- Duration minimumrest(String? station) =>
- Duration(hours: station == base ? 12 : 12, minutes: 0, seconds: 0);
- static Duration postflight = const Duration(minutes: 30);
- static Duration preflight = const Duration(minutes: 60);
- static Duration maxdutylast7 = const Duration(hours: 60);
- static Duration maxdutylast14 = const Duration(hours: 110);
- static Duration maxdutylast28 = const Duration(hours: 190);
- static Duration maxfltlast28 = const Duration(hours: 100);
- static Duration maxfltlastyear = const Duration(hours: 900);
- static Duration maxfltlast12 = const Duration(hours: 1000);
- Duration travelling(String station) {
- if (base == station) return Duration.zero;
- switch (station) {
- case "DSS":
- return const Duration(minutes: 60);
- case "ORY":
- return const Duration(minutes: 5);
- default:
- return const Duration(minutes: 30);
- }
- }
- String base;
- List<FtlDutyDetails> dutiesDetails = [];
- List<Duty> clDuties = [];
- List<FtlDuty> duties = [];
- // List<FtlDutyTotal> dutiesLength = [];
- List<DTInterval> get dutiesAsInterval => duties
- .map((e) =>
- (e.type != FtlDutyType.other) ? DTInterval(e.start!, e.end!) : null)
- .whereNotNull()
- .toList();
- List<DTInterval> get stdbyAsInterval => dutiesDetails
- .map((e) => (e.type == FtlDutyDetailsType.standby)
- ? DTInterval(e.start!, e.end!)
- : null)
- .whereNotNull()
- .toList();
- List<DTInterval> get fltsAsInterval => dutiesDetails
- .map((e) => (e.type == FtlDutyDetailsType.flight)
- ? DTInterval(e.start!, e.end!)
- : null)
- .whereNotNull()
- .toList();
- bool frms;
- Ftl({required this.dutiesDetails, required this.base, this.frms = false});
- Ftl.fromCrewlink(
- {required this.clDuties, required this.base, this.frms = false}) {
- for (Duty one in clDuties) {
- switch (one.type) {
- case "flight":
- dutiesDetails.add(
- FtlDutyDetails(
- start: one.start,
- end: one.end,
- placeStart: one.data["dep"],
- placeEnd: one.data["des"],
- type: FtlDutyDetailsType.flight,
- label: one.data["label"],
- ),
- );
- case "dhflight":
- dutiesDetails.add(
- FtlDutyDetails(
- start: one.start,
- end: one.end,
- placeStart: one.data["dep"],
- placeEnd: one.data["des"],
- type: FtlDutyDetailsType.dhflight,
- label: one.data["label"],
- ),
- );
- case "dhlimo":
- dutiesDetails.add(
- FtlDutyDetails(
- start: one.start,
- end: one.end,
- placeStart: one.data["dep"],
- placeEnd: one.data["des"],
- type: FtlDutyDetailsType.dhlimo,
- label: one.data["label"],
- ),
- );
- case "ground":
- if (["SBY1", "SBY2", "SBY3"].contains(one.data["label"])) {
- dutiesDetails.add(
- FtlDutyDetails(
- start: one.start,
- end: one.end,
- placeStart: one.data["dep"],
- // placeEnd: one.data["des"],
- type: FtlDutyDetailsType.standby,
- label: one.data["label"],
- ),
- );
- } else if (["R0"].contains(one.data["label"])) {
- //reserve is not duty
- dutiesDetails.add(
- FtlDutyDetails(
- start: one.start,
- end: one.end,
- placeStart: one.data["dep"],
- // placeEnd: one.data["des"],
- type: FtlDutyDetailsType.reserve,
- label: one.data["label"],
- ),
- );
- } else if ((one.data["actype"] ?? "") != "") {
- dutiesDetails.add(
- FtlDutyDetails(
- start: one.start,
- end: one.end,
- placeStart: one.data["dep"],
- // placeEnd: one.data["des"],
- type: FtlDutyDetailsType.sim,
- label: one.data["label"],
- ),
- );
- } else if (one.start != null &&
- one.end != null &&
- one.end!.diff(one.start!, unit: Unit.hour).abs() < 12) {
- dutiesDetails.add(
- FtlDutyDetails(
- start: one.start,
- end: one.end,
- placeStart: one.data["dep"],
- // placeEnd: one.data["des"],
- type: FtlDutyDetailsType.ground,
- label: one.data["label"],
- ),
- );
- } else {
- // print("ftl: constr: unknown ${one.date} ${one.type} ${one.data}");
- }
- case "checkin":
- dutiesDetails.add(
- FtlDutyDetails(
- start: one.start,
- end: one.end,
- placeStart: one.data["dep"],
- // placeEnd: one.data["des"],
- type: FtlDutyDetailsType.preflight,
- ),
- );
- case "checkout":
- dutiesDetails.add(
- FtlDutyDetails(
- start: one.start,
- end: one.end,
- // placeStart: one.data["dep"],
- placeEnd: one.data["des"],
- type: FtlDutyDetailsType.postflight,
- ),
- );
- case "credit":
- case "wholeday":
- break;
- default:
- // print("ftl: unk: ${one.date} ${one.type} ${one.data}");
- }
- }
- }
- Jiffy? _calcPostflight(FtlDutyDetails xduty) {
- if (xduty.end != null &&
- [FtlDutyDetailsType.flight, FtlDutyDetailsType.dhflight]
- .contains(xduty.type)) {
- return xduty.end!.addDuration(postflight);
- } else {
- return xduty.end;
- }
- }
- Jiffy? _calcPreflight(FtlDutyDetails xduty) {
- if (xduty.start != null &&
- [
- FtlDutyDetailsType.flight,
- FtlDutyDetailsType.dhflight,
- FtlDutyDetailsType.dhlimo,
- FtlDutyDetailsType.sim,
- ].contains(xduty.type)) {
- return (base == (xduty.placeStart ?? ""))
- ? xduty.start!.subtractDuration(const Duration(minutes: 60))
- : (xduty.start!.subtractDuration(preflight));
- } else {
- return xduty.start;
- }
- }
- final List<FtlDutyDetailsType> _fdpList = [
- FtlDutyDetailsType.flight,
- FtlDutyDetailsType.dhflight,
- FtlDutyDetailsType.dhlimo
- ];
- //calcul duties
- calcduties({Jiffy? date}) {
- log("FTL: start calcduties");
- duties.clear();
- final t1 = Jiffy.now();
- _calcDuty();
- log("FTL: start _calcfdpmax");
- _calcfdpmax();
- log("FTL: start _clacdutytotal");
- // _calcDutyTotal();
- log("FTL: start calclegal");
- _calcLegal(date: date);
- final t4 = Jiffy.now();
- print(
- "ftl: calcduties calculation : ${t4.diff(t1, unit: Unit.second, asFloat: true)} ms");
- log("FTL: finish calcduties");
- // return [duties, dutiesDetails, dutiesLength];
- return [duties, dutiesDetails];
- }
- _calcDuty() {
- for (int i = 0; i < dutiesDetails.length; i++) {
- var last = i > 0 ? dutiesDetails[i - 1] : null;
- final checkin = ((last?.type == FtlDutyDetailsType.preflight)
- ? dutiesDetails[i - 1].start
- : null);
- final newduty =
- _addDutyDetail(index: i, duty: FtlDuty(), checkin: checkin);
- //print("${dutiesDetails[i].type} $newduty");
- if (newduty != null) {
- if (duties.isEmpty) {
- //first duty
- duties.add(newduty);
- } else if ((newduty.start!.isAfter(duties.last.end!)) &&
- (duties.last.type == FtlDutyType.other ||
- newduty.type == FtlDutyType.other)) {
- //last and new are not duty nor fdp (other=>stdby or rsrv)
- duties.add(newduty);
- } else if ((newduty.start!
- .subtractDuration(travelling(newduty.placeStart ?? base))
- .isAfter(
- duties.last.end!
- .addDuration(travelling(duties.last.placeEnd ?? base)),
- )) &&
- // duties.last.type == FtlDutyType.duty &&
- newduty.type == FtlDutyType.duty) {
- //last is duty
- duties.add(newduty);
- } else if (newduty.start!.diff(duties.last.end!, unit: Unit.minute) <
- minimumrest(duties.last.placeEnd).inMinutes) {
- //diff < minimumrest
- //ilhim
- final mergedduty = _addDutyDetail(
- index: i,
- duty: duties.isNotEmpty ? duties.last : FtlDuty(),
- checkin: checkin);
- duties[duties.length - 1] = mergedduty!;
- } else {
- //jdida
- duties.add(newduty);
- }
- }
- }
- }
- _calcfdpmax() {
- for (var i = 0; i < duties.length; i++) {
- FtlDuty duty = duties[i];
- FtlDuty? lastduty = i == 0 ? null : duties[i - 1];
- if (duty.type == FtlDutyType.fdp) {
- String acclim = _acclimatized(
- tzoffset: tzDiff(duty.placeStart!, lastduty?.placeEnd ?? base,
- duty.start!.dateTime),
- timeElapsed:
- lastduty?.end!.dateTime.difference(duty.start!.dateTime) ??
- const Duration());
- duties[i].acclim = acclim;
- duties[i].reftime = changeTz(duty.start!, duty.placeStart ?? base).Hm;
- Jiffy reftime = duty.start!;
- if (duty.reportdelay2 != null && duty.reportdelay1 != null) {
- if (duty.start!.dateTime
- .difference(duty.reportdelay2!.dateTime)
- .inMinutes <
- (4 * 60)) {
- reftime = duty.start!;
- duty.start =
- duty.notification2!.add(hours: 1).min(duty.reportdelay1!);
- } else {
- reftime = duty.reportdelay2!;
- duty.start = duty.reportdelay2!;
- }
- } else if (duty.reportdelay1 != null) {
- if (duty.start!.dateTime
- .difference(duty.reportdelay1!.dateTime)
- .inMinutes <
- (4 * 60)) {
- reftime = duty.start!;
- duty.start = duty.reportdelay1!;
- } else {
- reftime = duty.reportdelay1!;
- duty.start = duty.reportdelay1!;
- }
- }
- switch (acclim) {
- case "D":
- // print("${duty.start?.yMMMd} ${duty.start?.toUtc().Hm}");
- duties[i].fdpMax = fdpMaxBasic(
- reftime: reftime, iata: duty.placeStart, sectors: duty.sectors);
- duties[i].fdpExtMax = fdpMaxExtBasic(
- reftime: reftime, iata: duty.placeStart, sectors: duty.sectors);
- case "B":
- duties[i].fdpMax = fdpMaxBasic(
- reftime: reftime,
- iata: lastduty?.placeEnd ?? lastduty?.placeStart ?? base,
- sectors: duty.sectors);
- duties[i].fdpExtMax = fdpMaxExtBasic(
- reftime: reftime,
- iata: lastduty?.placeEnd ?? lastduty?.placeStart ?? base,
- sectors: duty.sectors);
- case "X":
- if (frms) {
- duties[i].fdpMax = fdpMaxUnk(
- reftime: reftime,
- iata: duty.placeStart,
- sectors: duty.sectors);
- } else {
- duties[i].fdpMax = fdpMaxUnkFrms(
- reftime: reftime,
- iata: duty.placeStart,
- sectors: duty.sectors);
- }
- break;
- default:
- }
- }
- }
- }
- _calcLegal({Jiffy? date}) {
- if (date == null) {
- for (var i = 0; i < duties.length; i++) {
- _checklegal(index: i);
- }
- } else {
- duties.forEachIndexed((i, e) {
- if (duties[i]
- .start!
- .isSameOrAfter(date.startOf(Unit.day).subtract(days: 1))) {
- _checklegal(index: i);
- }
- });
- }
- }
- Duration _sumDuration(List<DTInterval> x) =>
- Duration(milliseconds: x.map((e) => e.duration.inMilliseconds).sum);
- List<DTInterval> breaks(FtlDuty duty) {
- List<DTInterval> out = [];
- for (var e in duty.dutiesDetailsIndex) {
- final dutydetail = dutiesDetails[e];
- final deptravel = (base == (dutydetail.placeStart ?? ""))
- ? Duration.zero
- : travelling(dutydetail.placeStart ?? "");
- final destravel = (base == (dutydetail.placeEnd ?? ""))
- ? Duration.zero
- : travelling(dutydetail.placeEnd ?? dutydetail.placeStart ?? "");
- final start = _calcPreflight(dutydetail)!.subtractDuration(deptravel);
- final finish = _calcPostflight(dutydetail)!.addDuration(destravel);
- if (finish.isAfter(start)) {
- out.add(DTInterval(start, finish));
- }
- }
- return duty.interval.minusmany(out);
- }
- _checklegal({required int index}) {
- FtlDuty duty = duties[index];
- final date = changeTz(duty.start!, base).endOf(Unit.day);
- // log("ftl: dutylenght date=${date.format(pattern: "ddMMMyy HHmm")} \n date-7=${date.subtract(days: 7 - 1).startOf(Unit.day).format(pattern: "ddMMMyy HHmm")}");
- // print("${date.yMEd}");
- final dutyLength = FtlDutyTotal(
- date: date, dutyLength: Duration.zero, fltLength: Duration.zero)
- ..dutylast7 = _sumDuration(
- DTInterval(date.subtract(days: 7 - 1).startOf(Unit.day), date)
- .intersectionmany(dutiesAsInterval))
- .add(_sumDuration(
- DTInterval(date.subtract(days: 7 - 1).startOf(Unit.day), date)
- .intersectionmany(stdbyAsInterval))
- .multiply(0.25))
- ..dutylast14 = _sumDuration(
- DTInterval(date.subtract(days: 14 - 1).startOf(Unit.day), date)
- .intersectionmany(dutiesAsInterval))
- .add(_sumDuration(DTInterval(
- date.subtract(days: 14 - 1).startOf(Unit.day), date)
- .intersectionmany(stdbyAsInterval))
- .multiply(0.25))
- ..dutylast28 = _sumDuration(
- DTInterval(date.subtract(days: 28 - 1).startOf(Unit.day), date)
- .intersectionmany(dutiesAsInterval))
- .add(_sumDuration(DTInterval(
- date.subtract(days: 28 - 1).startOf(Unit.day), date)
- .intersectionmany(stdbyAsInterval))
- .multiply(0.25))
- ..fltlast28 = _sumDuration(
- DTInterval(date.subtract(days: 28 - 1).startOf(Unit.day), date)
- .intersectionmany(fltsAsInterval))
- ..fltlast12 = _sumDuration(DTInterval(
- date.endOf(Unit.month).subtract(months: 12).startOf(Unit.month),
- date)
- .intersectionmany(fltsAsInterval))
- ..fltyear = _sumDuration(DTInterval(date.startOf(Unit.year), date)
- .intersectionmany(fltsAsInterval));
- duty.dutyTotal = dutyLength;
- // print("${date.yMEd} ${dutyLength.fltyear?.tohhmm}");
- // dutylast7,
- if ((dutyLength.dutylast7?.inMinutes ?? 0) > maxdutylast7.inMinutes) {
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.dutylast7,
- legalCauseMsg:
- "Max duty in 7 days ending ${duty.start?.format(pattern: "ddMMMyy")} exceeded: ${dutyLength.dutylast7?.tohhmm} / ${maxdutylast7.inHours}h"));
- }
- // dutylast14,
- if ((dutyLength.dutylast14?.inMinutes ?? 0) > maxdutylast14.inMinutes) {
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.dutylast14,
- legalCauseMsg:
- "Max duty in 14 days ending ${duty.start?.format(pattern: "ddMMMyy")} exceeded: ${dutyLength.dutylast14?.tohhmm} / ${maxdutylast14.inHours}h"));
- }
- // dutylast28,
- if ((dutyLength.dutylast28?.inMinutes ?? 0) > maxdutylast28.inMinutes) {
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.dutylast28,
- legalCauseMsg:
- "Max duty in 28 days ending ${duty.start?.format(pattern: "ddMMMyy")} exceeded: ${dutyLength.dutylast28?.tohhmm} / ${maxdutylast28.inHours}h"));
- }
- // fltlast28,
- if ((dutyLength.fltlast28?.inMinutes ?? 0) > maxfltlast28.inMinutes) {
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.fltlast28,
- legalCauseMsg:
- "Max flight hours in 28 days ending ${duty.start?.format(pattern: "ddMMMyy")} exceeded: ${dutyLength.fltlast28?.tohhmm} / ${maxfltlast28.inHours}h"));
- }
- // fltyear,
- if ((dutyLength.fltyear?.inMinutes ?? 0) > maxfltlastyear.inMinutes) {
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.fltyear,
- legalCauseMsg:
- "Max flight hours in calendar year ${duty.start?.format(pattern: "yyyy")} exceeded: ${dutyLength.fltyear?.tohhmm} / ${maxfltlastyear.inHours}h"));
- }
- // fltlast12,
- if ((dutyLength.fltlast12?.inMinutes ?? 0) > maxfltlast12.inMinutes) {
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.fltlast12,
- legalCauseMsg:
- "Max flight hours in 12 months ending ${duty.start?.format(pattern: "ddMMMyy")} exceeded: ${dutyLength.fltlast12?.tohhmm} / ${maxfltlast12.inHours}h"));
- }
- // fdpmax,
- if (duty.type == FtlDutyType.fdp && duty.fdpMax != null) {
- if (duty.fdpLength.inMinutes <= duty.fdpMax!.inMinutes) {
- } else if (duty.fdpExtMax != null &&
- duty.fdpLength.inMinutes <= duty.fdpExtMax!.inMinutes) {
- int nbFdpExt = 0;
- for (var i = index;
- i >= 0 &&
- DTInterval(
- changeTz(duty.start!, base)
- .startOf(Unit.day)
- .subtract(days: 7),
- changeTz(duty.end!, base))
- .isOverlap(DTInterval(duties[i].start!, duties[i].end!));
- i--) {
- if (duties[i].fdpExt) nbFdpExt++;
- }
- if (nbFdpExt < 2) {
- duty.fdpExt = true;
- duties[index] = duty;
- } else {
- //nbext more than2 in 7days
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.fdpmax,
- legalCauseMsg:
- "FDP Ext used more than twice in 7 days. ${duty.fdpLength.tohhmm} / ${duty.fdpMax?.tohhmm} / ext${duty.fdpExtMax?.tohhmm}"));
- }
- } else if (breaks(duty).any((e) => (e.duration.inHours > 3)
- //&&
- // ((duty.fdpLength.inMinutes) <=
- // (e.duration
- // .multiply(0.5)
- // .add(duty.fdpMax ?? Duration.zero)
- // .inMinutes)))) {
- )) {
- //split duty check
- final fdpmaxbreak = breaks(duty)
- .map((e) => ((e.duration.inHours > 3)
- ? ((e.duration.multiply(0.5).add(duty.fdpMax ?? Duration.zero)))
- : null))
- .whereNotNull();
- if (fdpmaxbreak.isNotEmpty && duty.fdpLength < fdpmaxbreak.first) {
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.fdpmax,
- condition:
- "A break on ground must be considered to extend FDP Max to ${fdpmaxbreak.first.tohhmm}, ",
- legalCauseMsg: "FDP is: ${duty.fdpLength.tohhmm}"));
- } else {
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.fdpmax,
- legalCauseMsg:
- "FDP Max is excedeed even with ground break of ${breaks(duty).map((e) => ((e.duration.inHours > 3) ? (e.duration) : null)).whereNotNull().first.tohhmm}. FDP:${duty.fdpLength.tohhmm}/Max:${fdpmaxbreak.first.tohhmm}"));
- }
- } else {
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.fdpmax,
- legalCauseMsg:
- "FDP is ${duty.fdpLength.tohhmm}/Max:${duty.fdpMax?.tohhmm}"));
- }
- }
- // restfdp without ext,
- final FtlDuty? lastduty = (index > 0) ? duties[index - 1] : null;
- // log("ftl: checking duty <${duty}> lastduty ended <${lastduty}>");
- if (lastduty != null &&
- duty.type == FtlDutyType.fdp &&
- (lastduty.type == FtlDutyType.fdp ||
- lastduty.type == FtlDutyType.duty ||
- lastduty.dutiesDetailsIndex.any(
- (e) => dutiesDetails[e].type == FtlDutyDetailsType.standby))) {
- final rest = minimumrest(lastduty.placeEnd)
- .max(duty.end!.dateTime.difference(duty.start!.dateTime));
- duties[index - 1].restends = lastduty.end!.addDuration(rest);
- if (lastduty.restends!.isAfter(duty.start!)) {
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.restfdp,
- legalCauseMsg:
- "Need more rest before starting FDP. rest: ${duty.start!.dateTime.difference(lastduty.end!.dateTime).tohhmm} / ${rest.tohhmm}"));
- }
- }
- // restfdp with ext,
- duty = duties[index];
- FtlDuty? nextduty = (index < duties.length - 1) ? duties[index + 1] : null;
- if (duty.fdpExt) {
- final restbefore = (lastduty == null)
- ? null
- : minimumrest(lastduty.placeEnd)
- .max(lastduty.end!.dateTime.difference(lastduty.start!.dateTime));
- final restafter = (nextduty == null)
- ? null
- : minimumrest(duty.placeEnd)
- .max(duty.end!.dateTime.difference(duty.start!.dateTime));
- //+2h rest before && 2h rest after
- if ((lastduty == null ||
- duty.start!.dateTime
- .difference(lastduty.end!.dateTime)
- .inMinutes >=
- (restbefore?.inMinutes ?? 0) + (2 * 60)) &&
- (nextduty == null ||
- nextduty.start!.dateTime
- .difference(duty.end!.dateTime)
- .inMinutes >=
- (restafter?.inMinutes ?? 0) + (60 * 2))) {
- }
- //+4h rest after
- else if ((nextduty == null ||
- nextduty.start!.dateTime.difference(duty.end!.dateTime).inMinutes >=
- (restafter?.inMinutes ?? 0) + (60 * 4))) {
- }
- //not enough rest
- else {
- duty.restends = duty.end!.addDuration(restafter!).add(hours: 4);
- duties[index] = duty;
- nextduty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.restfdp,
- legalCauseMsg:
- "Need more rest after ExtFDP before starting FDP on ${nextduty.start?.format(pattern: "ddMMMyy HH:mm")} rest: ${nextduty.start?.dateTime.difference(duty.end!.dateTime).tohhmm} / ${restafter.add(const Duration(hours: 4)).tohhmm}"));
- duties[index + 1] = nextduty;
- }
- }
- // restrecurrent,
- // 36h inc 2 local nights or (60h if 4 disruptive)
- const Duration maxdutyrecrest = Duration(hours: 168);
- Duration minrecrest = const Duration(hours: 36);
- bool foundrecrest = false;
- var i = index;
- int nbdisruptive = 0;
- DTInterval rest = DTInterval(duty.start!.toUtc(), duty.start!.toUtc());
- String restPlace = duty.placeStart!;
- DTInterval dutyl = DTInterval(duty.start!.toUtc(), duty.end!.toUtc());
- Jiffy lastdutystart = duty.start!.toUtc();
- DTInterval? recrest;
- //find last recrest
- while (i >= 1 && !(foundrecrest)) {
- i--;
- lastdutystart = duties[i + 1].start!.toUtc();
- dutyl = DTInterval(lastdutystart, duty.end!.toUtc());
- bool disruptive = (_isEarlyStart(duties[i + 1]) ||
- _isLateFinish(duties[i + 1]) ||
- _isNightDuty(duties[i + 1]));
- while (i >= 1 &&
- duties[i]
- .legals
- .any((e) => e.legalCause == FtlLegalCause.restrecurrent)) {
- i--;
- }
- rest = DTInterval(duties[i].end!.toUtc(), lastdutystart);
- restPlace = duties[i].placeStart!;
- foundrecrest = (rest.duration.inMinutes >= minrecrest.inMinutes) &&
- rest.contains(DTInterval.fromHm(
- apartir: rest.start.toUtc(),
- h: 23,
- m: 0,
- duration: const Duration(hours: 7, minutes: 59),
- ap: restPlace)) &&
- rest.contains(DTInterval.fromHm(
- apartir: rest.start.toUtc().add(days: 1),
- h: 23,
- m: 0,
- duration: const Duration(hours: 7, minutes: 59),
- ap: restPlace));
- if (foundrecrest) {
- //print(("ftl: recrest 36h: $rest");
- recrest = rest;
- }
- if (!foundrecrest && disruptive) {
- nbdisruptive++;
- }
- }
- //check if 60h rest needed
- //find recrest before last recrest
- int j = i;
- final duty2 = duties[j];
- bool foundrecrest2 = false;
- int nbdisruptive2 = 0;
- DTInterval rest2 = DTInterval(duty2.start!.toUtc(), duty2.start!.toUtc());
- String restPlace2 = duty2.placeStart!;
- Jiffy lastdutystart2 = duty2.start!.toUtc();
- if (recrest != null && duty.start!.isBefore(recrest.start.add(hours: 60))) {
- //print(("ftl 60h check: $duty");
- while (j >= 1 && !(foundrecrest2)) {
- j--;
- lastdutystart2 = duties[j + 1].start!.toUtc();
- while (j >= 1 &&
- duties[j]
- .legals
- .any((e) => e.legalCause == FtlLegalCause.restrecurrent)) {
- j--;
- }
- rest2 = DTInterval(duties[j].end!.toUtc(), lastdutystart2);
- restPlace2 = duties[j].placeStart!;
- foundrecrest2 = (rest2.duration.inMinutes >= minrecrest.inMinutes) &&
- rest2.contains(DTInterval.fromHm(
- apartir: rest2.start.toUtc(),
- h: 23,
- m: 0,
- duration: const Duration(hours: 7, minutes: 59),
- ap: restPlace2)) &&
- rest2.contains(DTInterval.fromHm(
- apartir: rest2.start.toUtc().add(days: 1),
- h: 23,
- m: 0,
- duration: const Duration(hours: 7, minutes: 59),
- ap: restPlace2));
- // if (foundrecrest) print((rest);
- bool disruptive2 = (_isEarlyStart(duties[j]) ||
- _isLateFinish(duties[j]) ||
- _isNightDuty(duties[j]));
- if (!foundrecrest2 && disruptive2) {
- nbdisruptive2++;
- }
- }
- //print(("ftl: duty 60h: nbdisruptive: $nbdisruptive2");
- }
- if (nbdisruptive2 >= 4) {
- const restlength = Duration(hours: 60);
- final lastduty = ((index >= 2 &&
- duties[index - 1]
- .legals
- .any((e) => e.legalCause == FtlLegalCause.restrecurrent))
- ? duties[index - 2]
- : duties[index - 1]);
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.restrecurrent,
- legalCauseMsg:
- "More than 4 disruptive duties found before recurrent rest. Need ${restlength.inHours}h rest. Next duty can be started after <${lastduty.end!.addDuration(restlength).max(DTInterval.fromHm(apartir: lastduty.end!.toUtc(), h: 23, m: 0, duration: const Duration(hours: 7, minutes: 59), ap: lastduty.placeEnd!).end.add(days: 1)).format(pattern: "ddMMMyy HH:mm")}>"));
- } else {
- // if (DTInterval(duties[i].start!, duty.end!).duration.inMinutes >
- // print("ftl nb disruptive $nbdisruptive");
- if (dutyl.duration.inMinutes > maxdutyrecrest.inMinutes) {
- final restlength =
- ((nbdisruptive <= 4) ? minrecrest : const Duration(hours: 60));
- final lastduty = ((index >= 2 &&
- duties[index - 1]
- .legals
- .any((e) => e.legalCause == FtlLegalCause.restrecurrent))
- ? duties[index - 2]
- : duties[index - 1]);
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.restrecurrent,
- legalCauseMsg:
- "More than ${maxdutyrecrest.inHours}h without recurrent rest. from <${rest.end.format(pattern: "ddMMMyy HH:mm")}> to <${duty.end!.format(pattern: "ddMMMyy HH:mm")}> ${DTInterval(rest.end, duty.end!).duration.tohhmm}. ${maxdutyrecrest.inHours}h ends at <${rest.end.addDuration(maxdutyrecrest).format(pattern: "ddMMMyy HH:mm")}>. Need ${restlength.inHours}h rest. Next duty can be started after <${lastduty.end!.addDuration(restlength).max(DTInterval.fromHm(apartir: lastduty.end!.toUtc(), h: 23, m: 0, duration: const Duration(hours: 7, minutes: 59), ap: lastduty.placeEnd!).end.add(days: 1)).format(pattern: "ddMMMyy HH:mm")}>"));
- }
- }
- // 48h inc 2 local days
- final startmonth = changeTz(duty.start!.startOf(Unit.month), base);
- final endmonth = startmonth.endOf(Unit.month);
- if (duty.interval.isOverlap(
- DTInterval(endmonth.subtract(days: 4).add(minutes: 1), endmonth))) {
- int nb48inmonth = 0;
- List<FtlDuty> dutiesmonth = duties
- .where((e) => DTInterval(startmonth, endmonth)
- .isOverlap(DTInterval(e.start!, e.end!)))
- .toList();
- List<DTInterval> restmonth = DTInterval(startmonth, endmonth).minusmany(
- dutiesmonth
- .map((e) => DTInterval(changeTz(e.start!, e.placeStart!),
- changeTz(e.end!, e.placeStart!)))
- .toList());
- // print(restmonth);
- while (restmonth.isNotEmpty) {
- final e = restmonth.removeAt(0);
- final localdays = DTInterval.fromHm(
- apartir: e.start.toUtc(),
- h: 0,
- m: 0,
- duration: const Duration(hours: 47, minutes: 59),
- ap: base);
- if (e.contains(localdays)) {
- nb48inmonth++;
- // print(
- // "2localdays: ${localdays.start.format(pattern: "ddMMMyy HH:mm")} ${localdays.end.format(pattern: "ddMMMyy HH:mm")}");
- // print(
- // "in rest ${e.start.format(pattern: "ddMMMyy HH:mm")} ${e.end.format(pattern: "ddMMMyy HH:mm")}");
- restmonth = [...e.minus(localdays), ...restmonth];
- }
- }
- if (nb48inmonth == 0) {
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.restrecurrent,
- legalCauseMsg:
- "Before duty, need 2x2 local days rest from: ${startmonth.format(pattern: "ddMMMyy HH:mm")} to: ${endmonth.format(pattern: "ddMMMyy HH:mm")}"));
- } else if (nb48inmonth == 1 &&
- DTInterval(endmonth.subtract(days: 2).add(minutes: 1), endmonth)
- .isOverlap(duty.interval)) {
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.restrecurrent,
- legalCauseMsg:
- "Before duty, need 2x2 local days rest from: ${startmonth.format(pattern: "ddMMMyy HH:mm")} to: ${endmonth.format(pattern: "ddMMMyy HH:mm")}"));
- } else if (nb48inmonth == 1 &&
- DTInterval(endmonth.subtract(days: 4).add(minutes: 1),
- endmonth.subtract(days: 2))
- .isOverlap(duty.interval) &&
- DTInterval(endmonth.subtract(days: 4).add(minutes: 1),
- endmonth.subtract(days: 2))
- .intersectionmany(
- [...dutiesAsInterval, ...stdbyAsInterval]).isEmpty) {
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.restrecurrent,
- legalCauseMsg:
- "Before duty, need 2x2 local days rest from: ${startmonth.format(pattern: "ddMMMyy HH:mm")} to: ${endmonth.format(pattern: "ddMMMyy HH:mm")}"));
- }
- }
- // disruptive
- duty.lateFinish = _isLateFinish(duty);
- duty.earlyStart = _isEarlyStart(duty);
- duty.nightDuty = _isNightDuty(duty);
- if (duty.fdpStart != null &&
- lastduty != null &&
- base == lastduty.placeEnd &&
- (_isLateFinish(lastduty) || _isNightDuty(lastduty)) &&
- _isEarlyStart(duty)) {
- // log("ftl: found late finish or nighty <${duty.start!.format(pattern: "ddMMMyy HH:mm")}");
- if (!DTInterval(lastduty.end!, duty.start!).contains(DTInterval.fromHm(
- apartir: changeTz(lastduty.end!, lastduty.placeEnd!).toUtc(),
- h: 23,
- m: 0,
- duration: const Duration(hours: 7, minutes: 59),
- ap: duty.placeStart!))) {
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.disruptive,
- legalCauseMsg:
- "Disruptive duty; one local night of rest is required before early departure."));
- }
- }
- //consecutive night duty
- if (_isConsecutiveNight(duty, lastduty)) {
- var i = index;
- final monthint = DTInterval(
- changeTz(duty.start!.startOf(Unit.month), base),
- changeTz(duty.start!.endOf(Unit.month), base));
- int nbconsnight = 0;
- while (i > 0 &&
- duties[i]
- .interval
- .isOverlap(changeTzDt(monthint, duty.placeStart!))) {
- if (_isConsecutiveNight(duties[i], duties[i - 1])) {
- nbconsnight++;
- }
- i--;
- }
- if (nbconsnight > 1) {
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.disruptive,
- legalCauseMsg:
- "Consecutive nights is allowed only once per civil month."));
- } else {
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.disruptive,
- condition:
- "Two consecutive night duties; crew member agreement is required.",
- legalCauseMsg: ""));
- }
- }
- //local day rest if night duty
- if (lastduty != null && _isNightDuty(lastduty)) {
- if (DTInterval.fromHm(
- apartir: changeTz(lastduty.end!, lastduty.placeEnd!).toUtc(),
- h: 0,
- m: 0,
- duration: const Duration(hours: 23, minutes: 59),
- ap: lastduty.placeEnd!)
- .isOverlap(changeTzDt(duty.interval, duty.placeStart!))) {
- duty.legals.add(FtlLegal(
- legalCause: FtlLegalCause.disruptive,
- condition: "You can request a local day off after last night duty.",
- legalCauseMsg: ""));
- }
- }
- // if (duty.legals.isNotEmpty) {
- // log("${duty.legals.map((e) => e.legalCauseMsg)}");
- // }
- // duties[index] = duty;
- }
- bool _isConsecutiveNight(FtlDuty duty, FtlDuty? lastduty) =>
- _isNightDuty(duty) &&
- lastduty != null &&
- DTInterval.fromHm(
- apartir: changeTz(lastduty.end!, lastduty.placeEnd!).toUtc(),
- h: 2,
- m: 0,
- duration: const Duration(hours: 2, minutes: 59),
- ap: lastduty.placeEnd!)
- .isOverlap(lastduty.interval.toUtc());
- FtlDuty? _addDutyDetail(
- {required int index, required FtlDuty duty, Jiffy? checkin}) {
- final one = dutiesDetails[index];
- if (_fdpList.contains(one.type)) {
- duty.placeStart = duty.placeStart ?? one.placeStart;
- duty.placeEnd = one.placeEnd;
- //duty.start = duty.start ?? _calcPreflight(one).latest(checkin);
- duty.start = duty.start ?? checkin ?? _calcPreflight(one);
- duty.end = _calcPostflight(one);
- if (one.type == FtlDutyDetailsType.flight) {
- duty.type = FtlDutyType.fdp;
- duty.sectors++;
- duty.fdpStart = duty.start;
- duty.fdpEnd = one.end;
- } else {
- duty.type = duty.type ?? FtlDutyType.duty;
- }
- duty.dutiesDetailsIndex.add(index);
- } else if ([FtlDutyDetailsType.standby, FtlDutyDetailsType.reserve]
- .contains(one.type)) {
- duty.placeStart = duty.placeStart ?? one.placeStart;
- duty.placeEnd = one.placeStart;
- duty.start = duty.start ?? _calcPreflight(one);
- duty.end = one.end;
- duty.type = FtlDutyType.other;
- duty.dutiesDetailsIndex.add(index);
- } else if ([FtlDutyDetailsType.ground, FtlDutyDetailsType.sim]
- .contains(one.type)) {
- duty.placeStart = duty.placeStart ?? one.placeStart;
- duty.placeEnd = one.placeStart;
- duty.start = duty.start ?? _calcPreflight(one);
- duty.end = one.end;
- duty.type = duty.type ?? FtlDutyType.duty;
- duty.dutiesDetailsIndex.add(index);
- } else {
- return null;
- }
- return duty;
- }
- static bool _isEarlyStart(FtlDuty x) {
- //500 559
- // print(
- // "$x is earlystart: ${DTInterval.fromHm(apartir: changeTz(x.start!, x.placeStart!), h: 5, m: 0, duration: const Duration(minutes: 59)).include(x.start!)}");
- return (x.fdpStart != null &&
- DTInterval.fromHm(
- apartir: changeTz(x.start!.subtract(hours: 24), x.placeStart!)
- .toUtc(),
- h: 5,
- m: 0,
- duration: const Duration(minutes: 59),
- ap: x.placeStart!)
- .include(changeTz(x.start!, x.placeStart!)));
- }
- static bool _isLateFinish(FtlDuty x) {
- //2300 159
- // print(
- // "$x is latefinish: ${DTInterval.fromHm(apartir: changeTz(x.start!, x.placeStart!), h: 23, m: 0, duration: const Duration(hours: 2, minutes: 59)).include(x.end!)}");
- return (x.type != FtlDutyType.other) &&
- (DTInterval.fromHm(
- apartir: changeTz(x.start!, x.placeStart!).toUtc(),
- h: 23,
- m: 0,
- duration: const Duration(hours: 2, minutes: 59),
- ap: x.placeStart!)
- .include(changeTz(x.end!, x.placeStart!)));
- }
- static bool _isNightDuty(FtlDuty x) {
- //200 459
- // print(
- // "$x is Night duty: ${DTInterval.fromHm(apartir: changeTz(x.start!, x.placeStart!), h: 2, m: 0, duration: const Duration(hours: 2, minutes: 59)).include(x.end!)}");
- return (x.type != FtlDutyType.other) &&
- ((DTInterval.fromHm(
- apartir: x.start!.toUtc(),
- h: 2,
- m: 0,
- duration: const Duration(hours: 2, minutes: 59),
- ap: x.placeStart!))
- .isOverlap(x.interval.toUtc()) ||
- (DTInterval.fromHm(
- apartir: x.start!.subtract(days: 1).toUtc(),
- h: 2,
- m: 0,
- duration: const Duration(hours: 2, minutes: 59),
- ap: x.placeStart!))
- .isOverlap(x.interval.toUtc()));
- }
- Duration tzDiff(String iata1, String iata2, DateTime sourceDateTime) {
- // print(Airports.instance.find(iata1)!.timezoneid);
- // print(Airports.instance.find(iata2)!.timezoneid);
- final iata1Location = tz.getLocation(Airports.find(iata1)!.timezoneid);
- final iata2Location = tz.getLocation(Airports.find(iata2)!.timezoneid);
- tz.TZDateTime iata1TZDateTime =
- tz.TZDateTime.from(sourceDateTime, iata1Location);
- tz.TZDateTime iata2TZDateTime =
- tz.TZDateTime.from(sourceDateTime, iata2Location);
- return Duration(
- minutes: (iata2TZDateTime.timeZoneOffset.inMinutes -
- iata1TZDateTime.timeZoneOffset.inMinutes)
- .abs());
- }
- String _acclimatized(
- {required Duration tzoffset, required Duration timeElapsed}) {
- if (tzoffset.inHours <= 2) {
- return "D";
- } else if (timeElapsed.inHours < 48) {
- return "B";
- } else if (tzoffset.inHours < 4 && timeElapsed.inHours >= 48) {
- return "D";
- } else if (tzoffset.inHours <= 6 && timeElapsed.inHours >= 72) {
- return "D";
- } else if (tzoffset.inHours <= 9 && timeElapsed.inHours >= 96) {
- return "D";
- } else if (tzoffset.inHours <= 12 && timeElapsed.inHours >= 120) {
- return "D";
- } else {
- return "X";
- }
- }
- static Jiffy changeTz(Jiffy jiffy, String iata) {
- return Jiffy.parseFromDateTime(tz.TZDateTime.from(jiffy.dateTime,
- tz.getLocation(Airports.find(iata)?.timezoneid ?? "UTC")));
- }
- static DTInterval changeTzDt(DTInterval dt, String iata) {
- return DTInterval(changeTz(dt.start, iata), changeTz(dt.end, iata));
- }
- Duration _fdpMax(
- {required reftime,
- String? iata,
- required int sectors,
- required Map<String, List<String>> tab}) {
- Jiffy reftjiffy = Jiffy.now();
- String reft = "";
- if (reftime is Jiffy && iata != null) {
- reftjiffy = reftime;
- reftjiffy = changeTz(reftjiffy, iata);
- reft = reftjiffy.format(pattern: "HHmm");
- } else if (reftime is DateTime && iata != null) {
- reftjiffy = Jiffy.parseFromDateTime(reftime);
- reftjiffy = changeTz(reftjiffy, iata);
- reft = reftjiffy.format(pattern: "HHmm");
- } else if (reftime is String && reftime.length == 4) {
- reft = reftime;
- } else if (reftime is String && iata != null) {
- reftjiffy = Jiffy.parse(reftime);
- reftjiffy = changeTz(reftjiffy, iata);
- reft = reftjiffy.format(pattern: "HHmm");
- } else {
- throw ("_fdpMax type of reftime unrecognized!!!");
- }
- // print("$iata $reft ${reftjiffy.toUtc().Hm}");
- for (String key in tab.keys) {
- List<String> intervalle = key.split("-");
- int cmph1 = reft.compareTo(intervalle[0]);
- int cmph2 = reft.compareTo(intervalle[1]);
- bool inint = (intervalle[0].compareTo(intervalle[1]) < 0)
- ? (cmph1 >= 0 && cmph2 <= 0)
- : ((cmph1 >= 0 || cmph2 <= 0));
- if (inint) {
- final List<String>? lres = tab[key];
- final int nrow = ((sectors == 1) ? 2 : sectors) - 2;
- final String? res =
- (lres != null && nrow < lres.length) ? lres[nrow] : null;
- if (res != null && res != "Not allowed") {
- return Duration(
- hours: int.parse(res.split(".")[0]),
- minutes: int.parse(res.split(".")[1]));
- }
- }
- }
- return Duration.zero;
- }
- Duration fdpMaxBasic(
- {required reftime, String? iata, required int sectors}) =>
- _fdpMax(reftime: reftime, iata: iata, sectors: sectors, tab: _fdpMaxTab);
- Duration fdpMaxExtBasic(
- {required reftime, String? iata, required int sectors}) =>
- _fdpMax(
- reftime: reftime, iata: iata, sectors: sectors, tab: _fdpMaxExtTab);
- Duration fdpMaxUnk({required reftime, String? iata, required int sectors}) =>
- _fdpMax(reftime: reftime, iata: iata, sectors: sectors, tab: _fdpUnkTab);
- Duration fdpMaxUnkFrms(
- {required reftime, String? iata, required int sectors}) =>
- _fdpMax(
- reftime: reftime, iata: iata, sectors: sectors, tab: _fdpUnkFrmsTab);
- //acclim
- final Map<String, List<String>> _fdpMaxTab = {
- "0600-1329": [
- "13.00",
- "12.30",
- "12.00",
- "11.30",
- "11.00",
- "10.30",
- "10.00",
- "09.30",
- "09.00",
- ],
- "1330-1359": [
- "12.45",
- "12.15",
- "11.45",
- "11.15",
- "10.45",
- "10.15",
- "09.45",
- "09.15",
- "09.00",
- ],
- "1400-1429": [
- "12.30",
- "12.00",
- "11.30",
- "11.00",
- "10.30",
- "10.00",
- "09.30",
- "09.00",
- "09.00",
- ],
- "1430-1459": [
- "12.15",
- "11.45",
- "11.15",
- "10.45",
- "10.15",
- "09.45",
- "09.15",
- "09.00",
- "09.00",
- ],
- "1500-1529": [
- "12.00",
- "11.30",
- "11.00",
- "10.30",
- "10.00",
- "09.30",
- "09.00",
- "09.00",
- "09.00",
- ],
- "1530-1559": [
- "11.45",
- "11.15",
- "10.45",
- "10.15",
- "09.45",
- "09.15",
- "09.00",
- "09.00",
- "09.00",
- ],
- "1600-1629": [
- "11.30",
- "11.00",
- "10.30",
- "10.00",
- "09.30",
- "09.00",
- "09.00",
- "09.00",
- "09.00",
- ],
- "1630-1659": [
- "11.15",
- "10.45",
- "10.15",
- "09.45",
- "09.15",
- "09.00",
- "09.00",
- "09.00",
- "09.00",
- ],
- "1700-0459": [
- "11.00",
- "10.30",
- "10.00",
- "09.30",
- "09.00",
- "09.00",
- "09.00",
- "09.00",
- "09.00",
- ],
- "0500-0514": [
- "12.00",
- "11.30",
- "11.00",
- "10.30",
- "10.00",
- "09.30",
- "09.00",
- "09.00",
- "09.00",
- ],
- "0515-0529": [
- "12.15",
- "11.45",
- "11.15",
- "10.45",
- "10.15",
- "09.45",
- "09.15",
- "09.00",
- "09.00",
- ],
- "0530-0544": [
- "12.30",
- "12.00",
- "11.30",
- "11.00",
- "10.30",
- "10.00",
- "09.30",
- "09.00",
- "09.00",
- ],
- "0545-0559": [
- "12.45",
- "12.15",
- "11.45",
- "11.15",
- "10.45",
- "10.15",
- "09.45",
- "09.15",
- "09.00",
- ],
- };
- final Map<String, List<String>> _fdpMaxExtTab = {
- "0600-0614": [
- "Not allowed",
- "Not allowed",
- "Not allowed",
- "Not allowed",
- ],
- "0615-0629": [
- "13.15",
- "12.45",
- "12.15",
- "11.45",
- ],
- "0630-0644": [
- "13.30",
- "13.00",
- "12.30",
- "12.00",
- ],
- "0645-0659": [
- "13.45",
- "13.15",
- "12.45",
- "12.15",
- ],
- "0700-1329": [
- "14.00",
- "13.30",
- "13.00",
- "12.30",
- ],
- "1330-1359": [
- "13.45",
- "13.15",
- "12.45",
- "Not allowed",
- ],
- "1400-1429": [
- "13.30",
- "13.00",
- "12.30",
- "Not allowed",
- ],
- "1430-1459": [
- "13.15",
- "12.45",
- "12.15",
- "Not allowed",
- ],
- "1500-1529": [
- "13.00",
- "12.30",
- "12.00",
- "Not allowed",
- ],
- "1530-1559": [
- "12.45",
- "Not allowed",
- "Not allowed",
- "Not allowed",
- ],
- "1600-1629": [
- "12.30",
- "Not allowed",
- "Not allowed",
- "Not allowed",
- ],
- "1630-1659": [
- "12.15",
- "Not allowed",
- "Not allowed",
- "Not allowed",
- ],
- "1700-1729": [
- "12.00",
- "Not allowed",
- "Not allowed",
- "Not allowed",
- ],
- "1730-1759": [
- "11.45",
- "Not allowed",
- "Not allowed",
- "Not allowed",
- ],
- "1800-1829": [
- "11.30",
- "Not allowed",
- "Not allowed",
- "Not allowed",
- ],
- "1830-1859": [
- "11.15",
- "Not allowed",
- "Not allowed",
- "Not allowed",
- ],
- "1900-0359": [
- "Not allowed",
- "Not allowed",
- "Not allowed",
- "Not allowed",
- ],
- "0400-0414": [
- "Not allowed",
- "Not allowed",
- "Not allowed",
- "Not allowed",
- ],
- "0415-0429": [
- "Not allowed",
- "Not allowed",
- "Not allowed",
- "Not allowed",
- ],
- "0430-0444": [
- "Not allowed",
- "Not allowed",
- "Not allowed",
- "Not allowed",
- ],
- "0445-0459": [
- "Not allowed",
- "Not allowed",
- "Not allowed",
- "Not allowed",
- ],
- "0500-0514": [
- "Not allowed",
- "Not allowed",
- "Not allowed",
- "Not allowed",
- ],
- "0515-0529": [
- "Not allowed",
- "Not allowed",
- "Not allowed",
- "Not allowed",
- ],
- "0530-0544": [
- "Not allowed",
- "Not allowed",
- "Not allowed",
- "Not allowed",
- ],
- "0545-0559": [
- "Not allowed",
- "Not allowed",
- "Not allowed",
- "Not allowed",
- ]
- };
- final Map<String, List<String>> _fdpUnkTab = {
- "0000-2359": ["11.00", "10:30", "10.00", "09.30", "09.00", "09.00", "09.00"]
- };
- final Map<String, List<String>> _fdpUnkFrmsTab = {
- "0000-2359": ["12.00", "11.30", "11.00", "10.30", "10.00", "09.30", "09.00"]
- };
- }
- class FtlDuty {
- FtlDuty({this.start, this.end, this.placeStart, this.placeEnd, this.type});
- @override
- String toString() {
- return "${start?.yMEd} ${start?.Hm} >>> ${end?.Hm} : ${fdpLength.inMinutes > 0 ? "FDP" : "DUTY"} ${dutiesDetailsIndex.length}leg(s)";
- }
- Jiffy? start;
- Jiffy? end;
- DTInterval get interval => DTInterval(start!, end!);
- String? placeStart;
- String? placeEnd;
- FtlDutyType? type;
- List<int> dutiesDetailsIndex = [];
- Jiffy? fdpStart;
- Jiffy? fdpEnd;
- int sectors = 0;
- Duration? fdpMax;
- Duration? fdpExtMax;
- //Duration? fdpIfRExtMax;//if inflight rest rest
- bool fdpExt = false;
- List<FtlLegal> legals = [];
- Duration get dutyLength => (start != null && end != null)
- ? end!.dateTime.difference(start!.dateTime)
- : Duration.zero;
- Duration get fdpLength => (fdpStart != null && fdpEnd != null)
- ? fdpEnd!.dateTime.difference(fdpStart!.dateTime)
- : Duration.zero;
- // Duration? get rest => (dutyLength.max(Ftl.minimumrest(placeEnd)));
- Jiffy? restends;
- String? acclim;
- String? reftime;
- FtlDutyTotal? dutyTotal;
- //Jiffy? report;
- Jiffy? reportdelay1;
- Jiffy? notification1;
- Jiffy? reportdelay2;
- Jiffy? notification2;
- bool earlyStart = false;
- bool lateFinish = false;
- bool nightDuty = false;
- }
- class FtlLegal {
- FtlLegal(
- {required this.legalCause,
- required this.legalCauseMsg,
- this.legalIndex,
- this.condition});
- FtlLegalCause legalCause;
- String legalCauseMsg;
- int? legalIndex;
- String? condition;
- }
- enum FtlDutyType { duty, fdp, other }
- class FtlDutyDetails {
- FtlDutyDetails({
- this.start,
- this.end,
- this.placeStart,
- this.placeEnd,
- this.type,
- this.label,
- });
- Jiffy? start;
- Jiffy? end;
- DTInterval get interval => DTInterval(start!, end!);
- Duration get duration => end!.dateTime.difference(start!.dateTime);
- String? placeStart;
- String? placeEnd;
- FtlDutyDetailsType? type;
- String get typeString {
- switch (type) {
- case FtlDutyDetailsType.flight:
- return "FLT";
- case FtlDutyDetailsType.dhflight:
- return "DH-FLT";
- case FtlDutyDetailsType.dhlimo:
- return "DH-LIMO";
- case FtlDutyDetailsType.sim:
- return "SIM";
- case FtlDutyDetailsType.standby:
- return "STBY";
- case FtlDutyDetailsType.reserve:
- return "RSRV";
- case FtlDutyDetailsType.ground:
- return "GRND";
- default:
- return "UNK";
- }
- }
- String? label;
- }
- enum FtlDutyDetailsType {
- preflight,
- flight,
- postflight,
- dhflight,
- dhlimo,
- ground,
- standby,
- reserve,
- sim,
- // training,
- // other
- }
- class FtlDutyTotal {
- FtlDutyTotal(
- {required this.date, required this.dutyLength, required this.fltLength});
- Jiffy date;
- Duration dutyLength;
- Duration fltLength;
- Duration? dutylast7;
- Duration? dutylast14;
- Duration? dutylast28;
- Duration? fltlast28;
- Duration? fltyear;
- Duration? fltlast12;
- }
- enum FtlLegalCause {
- dutylast7,
- dutylast14,
- dutylast28,
- fltlast28,
- fltyear,
- fltlast12,
- fdpmax,
- restfdp,
- restrecurrent,
- disruptive
- //check duty length && flt length
- //check fdp leength
- //check rest before fdp
- //check recurrent rest
- //check disruptive sched
- }
|