From 8604ece1b3c5474700e43b8c6f9ead4d62777a87 Mon Sep 17 00:00:00 2001 From: TruongQuangSB <131350493+TruongQuangSB@users.noreply.github.com> Date: Fri, 20 Dec 2024 08:43:07 +0100 Subject: [PATCH] Modify Ssld examplar and Free reporting section (#1198) * Modify Ssld examplar and Free reporting section * Improve after review * Ssld: Update calculation for dissolution section * Improve after review * Improve after review * Modify examplar --- .../graph/TopologicalGraphServiceImpl.java | 7 +- .../org/eclipse/set/basis/graph/TopPath.java | 126 ++++++++++++++---- .../table/pt1/ssks/SignalSideDistance.java | 2 + .../feature/table/pt1/ssld/SsldColumns.java | 8 +- .../table/pt1/ssld/SsldTransformator.xtend | 120 ++++++++++------- .../data/export/excel/ssld_vorlage.xlsx | Bin 23092 -> 22950 bytes .../ppmodel/extensions/DwegExtensions.xtend | 108 ++------------- 7 files changed, 188 insertions(+), 183 deletions(-) diff --git a/java/bundles/org.eclipse.set.application/src/org/eclipse/set/application/graph/TopologicalGraphServiceImpl.java b/java/bundles/org.eclipse.set.application/src/org/eclipse/set/application/graph/TopologicalGraphServiceImpl.java index 21bf5556b..e7f38d7be 100644 --- a/java/bundles/org.eclipse.set.application/src/org/eclipse/set/application/graph/TopologicalGraphServiceImpl.java +++ b/java/bundles/org.eclipse.set.application/src/org/eclipse/set/application/graph/TopologicalGraphServiceImpl.java @@ -141,11 +141,8 @@ public Optional findShortestPath(final TopPoint from, return Optional.ofNullable( // DijkstraShortestPath.findPathBetween(graphView, fromNode, toNode)) - .map(p -> new TopPath( - p.getEdgeList().stream().map(Edge::edge).distinct() - .toList(), - getPathWeight(p), - p.getEdgeList().getFirst().getWeight())); + .map(p -> new TopPath(p.getEdgeList().stream().map(Edge::edge) + .distinct().toList(), getPathWeight(p), from)); } private static BigDecimal getPathWeight(final GraphPath path) { diff --git a/java/bundles/org.eclipse.set.basis/src/org/eclipse/set/basis/graph/TopPath.java b/java/bundles/org.eclipse.set.basis/src/org/eclipse/set/basis/graph/TopPath.java index ac8c50252..54ec8d9c9 100644 --- a/java/bundles/org.eclipse.set.basis/src/org/eclipse/set/basis/graph/TopPath.java +++ b/java/bundles/org.eclipse.set.basis/src/org/eclipse/set/basis/graph/TopPath.java @@ -24,6 +24,7 @@ public class TopPath { private final List edges; private final BigDecimal length; private final BigDecimal firstEdgeLength; + private final TopPoint startNode; /** * @param edges @@ -40,6 +41,46 @@ public TopPath(final List edges, final BigDecimal length, this.edges = edges; this.length = length; this.firstEdgeLength = firstEdgeLength; + this.startNode = determineStartNode(); + } + + /** + * @param edges + * ordered list of edges for this path + * @param length + * the total length of the path. may be less than the total + * length of the edges if the path does not cover the full extent + * of the edges + * @param startNode + * the start point of this path + */ + public TopPath(final List edges, final BigDecimal length, + final TopPoint startNode) { + this.edges = edges; + this.length = length; + this.startNode = startNode; + this.firstEdgeLength = determintFirstEdgeLength(); + } + + private TopPoint determineStartNode() { + try { + final TOP_Kante firstEdge = edges.getFirst(); + return new TopPoint(firstEdge, firstEdge.getTOPKanteAllg() + .getTOPLaenge().getWert().subtract(firstEdgeLength)); + } catch (final Exception e) { + throw new IllegalArgumentException( + "Can\'t find start node of TopPath"); //$NON-NLS-1$ + } + } + + private BigDecimal determintFirstEdgeLength() { + try { + return edges.getFirst().getTOPKanteAllg().getTOPLaenge().getWert() + .subtract(startNode.distance()); + } catch (final Exception e) { + throw new IllegalArgumentException( + "Can\'t find first edge length of TopPath"); //$NON-NLS-1$ + } } /** @@ -72,40 +113,14 @@ public BigDecimal firstEdgeLength() { * on the path */ public Optional getDistance(final TopPoint point) { - BigDecimal pathOffset = this.firstEdgeLength; - + BigDecimal pathOffset = firstEdgeLength; TOP_Kante previousEdge = null; for (final TOP_Kante edge : edges()) { final BigDecimal edgeLength = edge.getTOPKanteAllg().getTOPLaenge() .getWert(); - // If the point is on the current edge, check which direction to add if (point.edge().equals(edge)) { - // In top direction - if (previousEdge == null - || previousEdge.getIDTOPKnotenB().getValue() == edge - .getIDTOPKnotenA().getValue() - || previousEdge.getIDTOPKnotenA().getValue() == edge - .getIDTOPKnotenA().getValue()) { - final BigDecimal pointDistance = pathOffset - .add(point.distance()); - if (pointDistance.compareTo(BigDecimal.ZERO) < 0 - || pointDistance.compareTo(length) > 0) { - return Optional.empty(); - } - - return Optional.of(pointDistance); - } - - // against top direction - final BigDecimal pointDistance = pathOffset.add(edgeLength) - .subtract(point.distance()); - if (pointDistance.compareTo(BigDecimal.ZERO) < 0 - || pointDistance.compareTo(length) > 0) { - return Optional.empty(); - } - - return Optional.of(pointDistance); + return getDistance(point, pathOffset, previousEdge, edge); } // Point not on this edge, check next edge @@ -113,8 +128,61 @@ public Optional getDistance(final TopPoint point) { pathOffset = pathOffset.add(edgeLength); } previousEdge = edge; - } return Optional.empty(); } + + // If the point is on the current edge, check which direction to add + private Optional getDistance(final TopPoint point, + final BigDecimal pathOffset, final TOP_Kante previousEdge, + final TOP_Kante edge) { + final BigDecimal edgeLength = edge.getTOPKanteAllg().getTOPLaenge() + .getWert(); + // Point in first Edge + if (previousEdge == null) { + return getDistanceOnFirstEdge(point, edgeLength); + } + // In top direction + if (previousEdge.getIDTOPKnotenB().getValue() == edge.getIDTOPKnotenA() + .getValue() + || previousEdge.getIDTOPKnotenA().getValue() == edge + .getIDTOPKnotenA().getValue()) { + + final BigDecimal pointDistance = pathOffset.add(point.distance()); + if (pointDistance.compareTo(BigDecimal.ZERO) < 0 + || pointDistance.compareTo(length) > 0) { + return Optional.empty(); + } + + return Optional.of(pointDistance); + } + + // against top direction + final BigDecimal pointDistance = pathOffset.add(edgeLength) + .subtract(point.distance()); + if (pointDistance.compareTo(BigDecimal.ZERO) < 0 + || pointDistance.compareTo(length) > 0) { + return Optional.empty(); + } + + return Optional.of(pointDistance); + } + + private Optional getDistanceOnFirstEdge(final TopPoint point, + final BigDecimal edgeLength) { + if (point.distance().compareTo(startNode.distance()) == 0) { + return Optional.of(BigDecimal.ZERO); + } + + final boolean inTopDirection = edgeLength.subtract(startNode.distance()) + .compareTo(firstEdgeLength) == 0; + // Point lie before path start + if (inTopDirection == point.distance() + .compareTo(startNode.distance()) < 0) { + return Optional.empty(); + } + return Optional + .of(point.distance().subtract(startNode.distance()).abs()); + } + } diff --git a/java/bundles/org.eclipse.set.feature.table.pt1/src/org/eclipse/set/feature/table/pt1/ssks/SignalSideDistance.java b/java/bundles/org.eclipse.set.feature.table.pt1/src/org/eclipse/set/feature/table/pt1/ssks/SignalSideDistance.java index da9993126..4cae0728a 100644 --- a/java/bundles/org.eclipse.set.feature.table.pt1/src/org/eclipse/set/feature/table/pt1/ssks/SignalSideDistance.java +++ b/java/bundles/org.eclipse.set.feature.table.pt1/src/org/eclipse/set/feature/table/pt1/ssks/SignalSideDistance.java @@ -251,6 +251,8 @@ private static double getOpposideDistance( new Coordinate(transformX, transformY) }); final List relevantGeometries = Streams .stream(getContainer(potk).getGEOKante()) + .filter(geoKante -> GeoKanteExtensions + .topKante(geoKante) != null) .filter(geoKante -> GeoKanteExtensions .topKante(geoKante) != potk.getIDTOPKante().getValue()) .map(GEOKanteGeometryExtensions::getGeometry) diff --git a/java/bundles/org.eclipse.set.feature.table.pt1/src/org/eclipse/set/feature/table/pt1/ssld/SsldColumns.java b/java/bundles/org.eclipse.set.feature.table.pt1/src/org/eclipse/set/feature/table/pt1/ssld/SsldColumns.java index cb70a3b71..5c84361d5 100644 --- a/java/bundles/org.eclipse.set.feature.table.pt1/src/org/eclipse/set/feature/table/pt1/ssld/SsldColumns.java +++ b/java/bundles/org.eclipse.set.feature.table.pt1/src/org/eclipse/set/feature/table/pt1/ssld/SsldColumns.java @@ -99,14 +99,14 @@ public class SsldColumns { public static final String Manuell = "P"; /** - * Ssld.Aufloesung.Zielgleisabschnitt.Bezeichnung + * Ssld.Aufloesung.Aufloeseabschnitt.Bezeichnung */ - public static final String Zielgleisabschnitt_Bezeichnung = "Q"; + public static final String Aufloeseabschnitt_Bezeichnung = "Q"; /** - * Ssld.Aufloesung.Zielgleisabschnitt.Laenge + * Ssld.Aufloesung.Aufloeseabschnitt.Laenge */ - public static final String Zielgleisabschnitt_Laenge = "R"; + public static final String Aufloeseabschnitt_Laenge = "R"; /** * Ssld.Aufloesung.Verzoegerung diff --git a/java/bundles/org.eclipse.set.feature.table.pt1/src/org/eclipse/set/feature/table/pt1/ssld/SsldTransformator.xtend b/java/bundles/org.eclipse.set.feature.table.pt1/src/org/eclipse/set/feature/table/pt1/ssld/SsldTransformator.xtend index 0a2477498..c80d4facf 100644 --- a/java/bundles/org.eclipse.set.feature.table.pt1/src/org/eclipse/set/feature/table/pt1/ssld/SsldTransformator.xtend +++ b/java/bundles/org.eclipse.set.feature.table.pt1/src/org/eclipse/set/feature/table/pt1/ssld/SsldTransformator.xtend @@ -8,8 +8,10 @@ */ package org.eclipse.set.feature.table.pt1.ssld +import java.math.BigDecimal import java.math.RoundingMode import java.util.Set +import org.eclipse.set.basis.graph.TopPath import org.eclipse.set.basis.graph.TopPoint import org.eclipse.set.core.services.enumtranslation.EnumTranslationService import org.eclipse.set.core.services.graph.TopologicalGraphService @@ -17,12 +19,11 @@ import org.eclipse.set.feature.table.pt1.AbstractPlanPro2TableModelTransformator import org.eclipse.set.model.planpro.Ansteuerung_Element.Stell_Bereich import org.eclipse.set.model.planpro.Basisobjekte.Punkt_Objekt import org.eclipse.set.model.planpro.Fahrstrasse.Fstr_DWeg -import org.eclipse.set.model.planpro.Fahrstrasse.Fstr_Zug_Rangier import org.eclipse.set.model.planpro.Signale.Signal import org.eclipse.set.model.tablemodel.ColumnDescriptor import org.eclipse.set.ppmodel.extensions.container.MultiContainer_AttributeGroup import org.eclipse.set.ppmodel.extensions.utils.Case -import org.eclipse.set.utils.math.AgateRounding +import org.eclipse.set.ppmodel.extensions.utils.TopGraph import org.eclipse.set.utils.table.TMFactory import static org.eclipse.set.feature.table.pt1.ssld.SsldColumns.* @@ -38,8 +39,6 @@ import static extension org.eclipse.set.ppmodel.extensions.PunktObjektExtensions import static extension org.eclipse.set.ppmodel.extensions.SignalExtensions.* import static extension org.eclipse.set.ppmodel.extensions.UrObjectExtensions.* import static extension org.eclipse.set.utils.math.BigDecimalExtensions.* -import org.eclipse.set.ppmodel.extensions.utils.TopGraph -import java.math.BigDecimal /** * Table transformation for a Durchrutschwegtabelle (SSLD). @@ -57,45 +56,53 @@ class SsldTransformator extends AbstractPlanPro2TableModelTransformator { this.topGraphService = topGraphService } - def BigDecimal getShortestPathLength(Signal signal, Punkt_Objekt p) { + def TopPath getShortestPath(Signal signal, Punkt_Objekt p) { val points1 = signal.singlePoints.map[new TopPoint(it)] val points2 = p.singlePoints.map[new TopPoint(it)] return points1.flatMap [ pa | points2.map [ pb | - topGraphService.findShortestDistance(pa, pb) + topGraphService.findShortestPath(pa, pb) ] - ].filter[present].map[get].min + ].filter[present].map[get].minBy[length] } def String getFreigemeldetLaenge(Fstr_DWeg dweg, TopGraph topGraph, BigDecimal maxLength) { val startSignal = dweg?.fstrFahrweg?.start - var fmas = dweg?.FMAs.toList.filter [ - topGraph.isInWirkrichtungOfSignal(startSignal, it) - ].toList - // When not exists relevant FMA_Komponent/Gleis_Abschluss on the Fstr_Fahrweg of this DWeg, - // then take the FMA_Komponent/Gleis_Abschluss of this FMA_Anlage_Freimeldung, - // which in direction of the start Signal - if (fmas.empty) { - fmas = dweg?.fmaAnlageFreimeldung?.map[fmaGrenzen]?.flatten.toSet. - filter[topGraph.isInWirkrichtungOfSignal(startSignal, it)]. - toList - } - - val relevantDistances = fmas?.map [ - getShortestPathLength(dweg?.fstrFahrweg?.start, it) + val fmas = dweg?.fmaAnlageFreimeldung?.map[fmaGrenzen]?.flatten.toSet. + filter[topGraph.isInWirkrichtungOfSignal(startSignal, it)].toList + val pathFromSignalToFMA = fmas?.map [ + it -> getShortestPath(dweg?.fstrFahrweg?.start, it) ] - if (relevantDistances.isEmpty) { + if (pathFromSignalToFMA.isEmpty) { return "" } - val roundedDistance = AgateRounding.roundDown( - relevantDistances.max.doubleValue) - if (roundedDistance == 0.0) - throw new IllegalArgumentException("no path found") - else - return roundedDistance > maxLength.doubleValue - ? '''> «maxLength.toTableIntegerAgateDown»''' : roundedDistance.toString + + val relevantFmas = pathFromSignalToFMA.filter [ + dweg.isRelevantFma(key, value) + ].toList + + // When the free reporting section ending before the Dweg end, + // then take the FMA am nearst of the end of Dweg + if (relevantFmas.isEmpty) { + val fstrTOPKante = dweg.fstrFahrweg.bereichObjektTeilbereich.map [ + topKante + ] + val relevantPaths = pathFromSignalToFMA.filter [ + value.edges.forall[fstrTOPKante.contains(it)] + ] + if (relevantPaths.empty) { + throw new IllegalArgumentException("no path found") + } + return relevantPaths.maxBy[value.length].value.length. + toTableIntegerAgateDown + } + + val distance = relevantFmas.map[value.length].max + return distance > maxLength + ? '''> «maxLength.toTableIntegerAgateDown»''' + : distance.toTableIntegerAgateDown } override transformTableContent( @@ -309,10 +316,10 @@ class SsldTransformator extends AbstractPlanPro2TableModelTransformator { ] ) - // Q: Ssld.Aufloesung.Zielgleisabschnitt.Bezeichnung + // Q: Ssld.Aufloesung.Aufloeseabschnitt.Bezeichnung fillConditional( instance, - cols.getColumn(Zielgleisabschnitt_Bezeichnung), + cols.getColumn(Aufloeseabschnitt_Bezeichnung), dweg, [dweg.fstrDWegSpezifisch !== null], [ @@ -321,29 +328,14 @@ class SsldTransformator extends AbstractPlanPro2TableModelTransformator { ] ) - // R: Ssld.Aufloesung.Zielgleisabschnitt.Laenge + // R: Ssld.Aufloesung.Aufloeseabschnitt.Laenge fillConditional( instance, - cols.getColumn(Zielgleisabschnitt_Laenge), + cols.getColumn(Aufloeseabschnitt_Laenge), dweg, [dweg.fstrDWegSpezifisch !== null], [ - val fstrs = fstrZugRangier - if (fstrs.empty) { - return "" - } - - val distance = fstrs?.fold( - Double.valueOf(0.0), - [ Double current, Fstr_Zug_Rangier fstr | - Math.max(current, - fstrDWegSpezifisch?.IDFMAAnlageZielgleis?. - value?.IDGleisAbschnitt?.value?. - getOverlappingLength( - fstr.IDFstrFahrweg?.value).doubleValue) - ] - ) - return AgateRounding.roundDown(distance).toString + getZielGleisAbschnittLength(topGraph) ] ) @@ -365,4 +357,34 @@ class SsldTransformator extends AbstractPlanPro2TableModelTransformator { return factory.table } + + private def String getZielGleisAbschnittLength(Fstr_DWeg dweg, + TopGraph topGraph) { + val fstrs = dweg.fstrZugRangier + if (fstrs.empty) { + return "" + } + val startSignal = dweg?.fstrFahrweg?.start + val fmaAnlage = dweg.fstrDWegSpezifisch?.IDFMAAnlageZielgleis?.value + // The relevant FMA shouldn't lie on direction of start signal + val fmaKomponent = fmaAnlage.fmaGrenzen.filter [ + !topGraph.isInWirkrichtungOfSignal(startSignal, it) + ].toList + val pathFromSignalToFMA = fmaKomponent.map [ + startSignal.getShortestPath(it) + ] + + // Determine which path overlapping with Fstr and take the longest path + val overlappingPaths = fstrs.map [ fstr | + val fstrTOPKanten = fstr.IDFstrFahrweg?.value. + bereichObjektTeilbereich.map[IDTOPKante.value] + val relevantPath = pathFromSignalToFMA.filter [ path | + path.edges.forall[fstrTOPKanten.contains(it)] + ] + return relevantPath.map[it -> length].maxBy[value] + ].toList + // Take the longest overlapping path + val maxDistance = overlappingPaths.maxBy[value] + return maxDistance.value.toTableIntegerAgateDown + } } diff --git a/java/bundles/org.eclipse.set.feature/rootdir/data/export/excel/ssld_vorlage.xlsx b/java/bundles/org.eclipse.set.feature/rootdir/data/export/excel/ssld_vorlage.xlsx index 8db47c09d448095ddd31b359d46e5d444504c3c9..2e3feedc2aa081b0388e6b073940ef43c113efa9 100644 GIT binary patch delta 10346 zcmaia1yq#X_V*0k-6h?v!q6$*-H0^O4FZp-r1XH&AWA499nxLW(hS`xIUx1nd++yu zc=1>Jp)Z7?!qgz;ve0nDJxI4vl{p;5h zMYtoBe72Ph zi|n!y6NZZrb|r;KT~om7)k|wOtH_5`Ykit2j1WC^I$`UWgD+exor&)W=;A)N;`K8{ z_bABvDb;dIFAW6)=d{-E*X;|^#q@k8t~+@$*$N2^q5uyqHBqljVj`S#7VTd8`! z_fu87gIKWNe%Od;oZh=6Py$@7ay=$3)*r@Xz4|4wB&1@OMNKuAtei~mhiG1E2c1K^ zW>Ie|c8cxGbpFyG)C(>Q`8CjE9V0%&<@7COJa=T=j3vQ7f*+m z1WIBZN?K@`5r~|DYx-sncJv1%o0v~Rh~>d%1#A-$Ny;QS7Q77jhy&2+L<{kx9O2nW zJ7c%>kFs``9eb6wd=tKM0x5A6)#0VnnL&qc6%A6Y5Po|%S{^3&(GR~ES|oBE_6!I6 z)>Eq!YId*SgKX#Q7~k0mLe?lB{*8F7h6|ae!#?tslQoUN&2SB+v!xa8OnVbd2##6; zxX&EUuE?XwRliVJG+{ATB-_M$n0+Ikch6VbP*kwT{z`VG6&B=JAz{ou`yPD?tNE$Y z#I~a7x}B3VYiy^mZ1skW20X0W%c9|3nVMu^zB3{X+d&|MXA;Qr^R&S2OJ=^W(w;4L zI{Xyv-2(jpn87wyi_a$5sLZ&*E*i6Q`Qo`ol4cu;)dd{aQpmjZx#0b2E9wL$#1$R2 zC!Y%(LJ(D@gd$X9!Q1opuf-O4Gbi#B+{F&s(*;dQExa}9>I>j6E_teg1~47;tGp+t z+pn{r%Ad#^Ue7Nx7eqHTDIl;u$ikn>N0gF*;76h2R1jmFzS9_>R~)Y|h2vI$Q9Tea zWK4SvpWuN&_!iJ!s(XO0lgnL<+=$!XwjRNm-Drd9M;#n=i?tr=?o9*=#DEJG)2G*+ zv~Mp5nbnf}@G2k0)ia)X48g?2yWZ}UODdOC#_5JC;#qWXFneeY@L#Q54ou@QIN2qQ zN$ZKWQStX|8%g>N1c;GVo!K#opp<;r_S4MbGn@eN z;fL^2qDE@=%GFdTu?w@&9z5LA4xPYg;=A$}k9u~)uEmy|V5FuB>REboymK@4sj79o{|`^Eq#Wy7>(Y^!Xa?d2v3y?l#_ z2+bQIYdOvffn}L{YNI@FF>B-GrE zxY^zNSm8-ej^8D%zIk2p0QPPKVp=A2n{~m&V_^xWHZ zpd^~fALs(c(chy_uFDx1L`_Lk8u~k6Pmmy=tZ1|dux{b>LYJPuc!@aMQrEhwNKK1i z?IY5AMfcOFxg6LmNH?V zFsHn&@Q(1a{!XPT8MC4U^$eSPIcNCWA}?{tijLP4!q}O6CtDp2j}`N$fMA?x!O!Eh zW9-w^?by`t*zuT&DUMNLYtdD;Js=j2N4R&l9xV+B1eIXbAlJ>A=)XHW)=u>L$GQD9+cG`yYZU5D47hTBOjTZqxm$NHMYDx5K7PB4sKhu@oID5sSF za`ELNnl8;ndycH!UeV98XPr`CFx0WhJ@9TSA}AQzU<_=Xb;YTgf1%v)9eWGhbj1yn zVcn6$SU0$(3Kpk1V@>aF4jsqU*DPeNZ`pI1kapNp9FTUv1Y`4w4-cEgfs4I^t^DSj zHS44~6Jogfq_x}psJ&r>rKzZ6o|G&3@%06-8Hsjnq3qi}+)2K}+@qDMgt?i?Jhm<8 z{^^p01NKg5^R?nYCzf?*5#Y3BbeDb4*&Jr#cOF9{ep8y6c5VH&|6{<`QfXn-+rH~6 z%dy;}_r~*ICnbg~&pcKw?#rv?UkHmWF9h9QhF#=b@GHltWS9?%oNw)|oZ4k=w6$+Z zOg`Pc&f93U{(3QbeJXransT}n%?D5OBY=?|*BZV&w2tz4cAv4%tp|urlTmY_K3Ye3 zW7-{HN`FzJH3vz*9C*OyQuY1vROgeoafu7n5^P<2pqMYMv8E=*^;osuSiZLI@kx!^ z;JT9NZd0eP;2h-gC-%>2np|utF?yfajPs$-2M|> z?xZyWvVHcE5(0Df{!ZZGw3AQ<$3lgTfUV$c>{R+}fW)$!hr?^wyF`ypWY1=y*zurx z+%yiI#>>%xlF|J$*xtBa&Auwg{w^>%Ut_x!{7yJq!`;1ke9K9P`HsbC} zyMQ)6QMrla>pTTbPp(rXz2)nl`BY+jGxOPB-`+dvOv~?!cipU0-NHk!TD==D!LFS* zW*)=)306N>H{=5!)(B*kix5 z@hT4f zrMP~_YGurnX;$~hG|{Mv%q%K z@7^C^M&`<3N*2#c!i+fgP4w zp36`zk+DEJ$p_tnl}(l?1Zz8~_Ya@Tiw>Ci>|YH&hL`UyaJP!kJaYY$68JBq=^t=C zo$LC=7PxR+!x^Ynkn$nzLfozq940F$HMQ+xG~dJXr*{T3ET@A98|MOfKHHL_`n>*? za4K;e&zKr+A6X5mWwdTcE8C!J45rC4#$+wf%C+wriz>QofA+;=qk9K2!?>;G7`6-w zgS7FfxklbkUqN_V+xF1e^|i>{3OLp`wQ+y$Vj3>FG4JMLN|jE3c-qJt;~GVnzKpd| z+rm`h8cjL3j05*=VaT-kEK-Aknoqgc-#-QbE~aMtqW_^1@OPbxU$pLs)QBob{0HGb z=$6}l(wRwI?*HPXH!|hAp}t-lzA{Hqh(9%NGX0C{e`pT;TlGS$!ykk3bo->JkPiG9 zw-?&!OH`z_9mFMWFOBDxXmEWy#&*3n^vIV_GZlpWo6}G$~F7OQ|RTUAojnZVVC*IFnr8K?aiG!1@e$CbTzk7 z50ga#{n{>UXSc9tqKo+I6_XhvZ|L@W@gP78-Ty|>2FRA{L`zc`|)tC(5M#ietC479z4izPM#iAR$HXUk97J= zP5|Bc#cYK{Nc*qNAR6~uGx*Z}(hU1^Wmt!jp)Ft>w^x?wi$u1NZe(t^5a8+DB8g9J zHzLj9gJ-ZhOY-Gltl&R<^{-L-*C6fY??5C+_=TJ5Zm2bLiYL&m-(wtVj`^p+f5-#K zm*amgkl~jCy*6U3!{zz^4(xxyHTLQs;NQ^ze@kh1;O95*`wbNT4qVZM-{Zmfdps0E zf#2BYcjo!zp?(L*mSw*=uL+d~?%%NehpEYzdz({xT~Z4Enh}71=GHqek^K)Z`43JJ zzsHR5_n5KpBLRPl#{UAE`tO4=^Y_6h8vj4=0{(w}Gzj&j|0Q7CbM<%8aZ>j5_tv5Q zz3M*yRtxw=ew_bbhyLTtnIiDLBc8CEdw+Bje+-TZFy}U2tPpdiKExH67;1}K zyKy!2{Bt@0u6=xq30XcTbNzaVzW8e8ehYbb{&VdZ9?JdYT~b|6k+J5; z$9e#KO4O;D?Ma4QhCt1Dt%0FWvQdwME~_L)MH^Bxu$QFv!>xek%bVqPe-|D>J{27@ z2kNKASSQgDA0XOqpgXc`Y7;*e%0()SbEq$#GNcyK)o?VkC6y6PB{So_5ww z*8dv$xP6@*cMX+YCBk#pjU#<-%zt=U;mTMi;?$PF*Fk5{wMEq*Bs}%>~-eO zw`YfLu6|gabXT~TiCwhCzI}A{St8)5e<W zXelriFTxorQ`t>8JV4f!l#bPL;eBGMl0gkYnV}gqB^GI!)&3e#E8qr1`>0FO9fDm{ zXdvbL-A{4~!j15IhIxvwXRvVT!3&=RxwZUk$HB!@IVk7j0?)q-xV*wFTNbbjVo` z)NnZ*GuN^PLgK6i3`I|IIq2sc5(rXp+uK z>?bvUOPWje=_Kbsz|_}0k2ye=d{$pu&RKCROc@dtDrWGY{Y47R2BHy3`((qlk86C3 z4K9I6psa4gy%j8f;5?FCnIQ!l!`N5*N@$cb4V+B%-l@v~!>J)fddFY9Ojq_vRePAX zUoIqUus8YR1AdQ#sF6r!S3Mh!!h^1&C%M8SjwKGn45@5!F%BL{ty%q_T9@Zihu3H~ zqg(p0$u_HYc`qN!h0g4I%VpI)s%X+6?Ipui+-E3;m&dYq0g4p{DIxLQ^8R_Eo$|=e zs;XzywHcj?kG!i0rv@6}+-9lRqnJEfRN`-9IJuZvlt(N$>?R6NK_QT*$P8p@OHUJm zCv%-S`CcyuvFo&jUVrR$Z1Y+g$dlmYaOHTi*Yu%O8JGXK;>ATCtxym-$X%2yf`{0n%$CJUf3~tjM^PFv)xH=+ z{V97WPO0{qmssld`BM=mr{f;3LBCOAo_?&s63aK85S|+0N46=BOU$tw{K@8eMhrBY z>q#(HpCDb8KJz&CPTrfWk2ynxwJ`?d4!Eye(>QrYFMt`0S`EH1_QcKyyXdvD_p1hR_?9*;vP;K4{Y;a;jCwO%~iBZr=Es1KC(OQxAB}6o7hwC0y^e0#e(GhAQ zyII?G(!?RQ{rG|+a57DkO(qS~fi_@~%^jY502|~F!abqK>U#l>{8=?(!?0kdvfZ2e zXY^IlBLEK-vg*OKt^X%KA>a1apuzVOdPPjpg{SxLRgs&p(_NzuO0Y}IsgUg;Rt>?k ztLc3|Tg-AGX$E!*&?t~97}KB*L#7{5s-z}58654Owav2D9uA7Ygv@(JYqmUx?05Y< z*2_KI4+mfy7f~6;Q)OOdW2ea!31_~M+h=wH-!t4`5^v2+R!vVgIXCd3gFcT6_E~QmaM_F3S+S zAc?J;FxG~kB(755b34g{!0UQbq&xp%j2;FKchiZvS2sglEulDm+V zq5CUk60zM`g81WRyy|GL#b96fys*2)F%=rVpEPIMHU^h4A#{R)9L}!Hrp|~PzoSc+ z6r{~$A9QJwo8dm zUzVvv_9%#$4u5%JvM_TfhnT=hjTPURNqLeUII+gRAG1!B?aYaKG1Il}VLbc2fl&7{ zFDv{!or#l3kaqGhV4)CjMY2CkM$yB=7`*DAz&Xjgr*FCNJvD*qdna6TFCDY_%88>Ryj&tCmi_If*g3lrK58xp=Cp?E2*Hd;B_6cMB8jr<`Hj@+2vR8G9 zX$c+R`Nfto;4%h*p}xnz;G3C+rUv^%)E=SbhQL>d z9Z6!PGDhNo6P~@QvDph1JH9@;LS927M={$ZZR3FEBhpJE~Dk^Liys(Dhs&CStQ|FheBeAhv*tRI2IVQzUd#Q^O z^ljYes+f{GphfE8r-4Cm{P5k2j%Nu5v=669ig)&D@Y^^0J3))pC11?2{J8qbnTYmJjvPzL z&+~I|Dh-LW&e^$8O8ck6ycZfgxGDte9~Nhh$-kV>26oPDN~|SLGPd4}cqmW7QfvHG z>D$XkFIx#3LDXk3uo+&$q_)H7tnDm zHXu@VuoKV)D6eyt7d6*5G zdahI+1|8G)&y|B2V5KmSmi4$1gBZ0_22dA`Y*>fVLYW~(6P;--$F9;@j=kBvHOBH0 zt)K}B(uDlJvL}Px-CX79E2-bay_;Z5oII+A)kR{|3I#-AjeZowiH1tMs`cIIW2y|- z@4{a?TG>!Na0Vg1{jEZ%Bo7>!{1s5{6Nc1?sh~hhzo6`V9Jl(8?kFE94{KN{Ck5gO zBf!g6YLA&H>(Ogt9@KC#zkC_;Npl-@B)sU&JK43KY|b>8dXesfNchzRDvmf@8!3Qw zokUum3!$saN5X*>%n`XsrPFA;F6SpTGOV<-NTqK_`RZ98i@@Wx4+o4B3G;+ipL&QD z`;%f{LvI^DOYN~`owkDXIg<#9isJ!qnEHI=Yw=swm8xEfpi?;9oQfPL2bA}?r3Ahz zSD5P~h_e|xy65bW&FG-%Jt@P7DhqR=7=SyEkNA)}^}P_8isV2RCMP4<)AbFsk&1U! zZSq8ppSP|Y(AV}4D06&qaJKKT@Xlj(eycHJLBL-fsr&=}`y(?d*5k>bVkxvb5t-i2 zdq!73D`*84E&><>E9cp2#+`o*GeB2(xdEeo=P^klv6T-FN{n~}1hKk00t#q4c0J-p zp`Vc^HR#XGRd>Z zhti*I%)t9*eS=Y|AHR>m@HTKL?UXW}baKoI#Xh%dm1!vqqf3<)0nLJSU;PNq+5rTc z+R8QB-0Fs>k^L#)5N(aC0k_<@t&ha*M=?D3QKr3?JHXR%z z`{_riWvuXv2>;D4ZgGT1BuqOWqi}+?-ONwes`ce`Z%I*zGA$D!(*{TMw}9frOuiQ# zr>2E_vaC>krGBAxLHEi!9)BZbi;#<5%YQ;IXT0;$rU5?Kxcwz&w^re#gOnmLk+T*v zE-W|0UoMd`xh=OJ%G^|ySi*Ia=*RJaQF^>AHAIAT{KGaRnGhP-e?gZ1l#Dt zNtYER3Kn8$Fgc3ZF|>l_9OxZcMzhEDMqI#T$99p3Z>(N84t1|Nav%1%^hlnNIvAi` zVLIz73+v$a+UNe;=bF7VHsyOZioF{Z`;!G7DRb zcZYr|dcf`3PQNL99Z~8U+jKQQa zj6yak7Vj9Zbk^g))kl)e7(&3#yU9k$`vp#4+jlE8%#J#!w33IDQSF91t>pdW!0Z`} zQonHgvDZu&+68%F%KX60r!s+o5=OjCj+A#@1iotUZr21HB%|&PXM0tAG42pHqkc6z z0b`8VF<0~hif?P73okB#=g>_hNbFX{F1p47b+M9t$>KR_&O%$+6Kcdo7iCBLZ>qzVyjfK2f;0w z2|UAig}w+A!OXxK)L0xne#S*g)+l8WJFpl@^}5gv0uDR7yf*E)Dqf978hh~;mzPdK3)yll`kJv?Z(0R>nV z%B#l=Ws|{y9vEODa`A$ohpI$SECXVwzbp>TU*8Pk1A#2PJUm^T44fQ!>^-fW67-0n zc7_y)>%xD$m7vFia3l(CFkpht>Qf+QivNojfgv$;&yWIPO#&)o$OIkIr9do~{JBLA z%{0WL`oH+Ns}}fwl?OtQhV~gUKt&{Qpsog32tBgUAOm`+oG1>I=WaP54;8zURH#RR z)TVR?ELf1}4#o#aJu3fR;5@{DZro`pqfdb_qy{zDS3;W5`1c-_5C(~cGVt>r~&WQ&oHKb8dItlU;^bUXBQVgjS}EX@G>0{9qf-f{cLR2BStJ z1(YB&ym)>z@Vig=ekrUh`;Os^ppL(FQ|U_-V^&2yjiao!F`fk8bUkV!|z0Vf=)$EkQy7 zw2&OpTZbBzOTVJghERmz*RPE1K0NU61~8u{ZWdrZ(dLy+=v`KZAgfpz)-WU3G6y`x zTKQzE`%YN=vq4}6FGbonT{BmV;k_5!=;m>a%pPU}<(%Hwy4@$fdKZb$Sb{X2zwMel zl&$BKv#(UPsw5_LlL*!Sau|J>L5ribjv>#k`$1^rdv4S&y$hV_hR%;1^}UGPi(`Nj ztwe(S0XcPb!BX6Z=%zR|pSFnfX3L**mPA=&)(qc%_|i`wbb;tbday|jjM(*J)W&EE zT|N-B5%x~H^H;?xcA+O&qYa2`)Yj(Lh}jV7*Lz9?Y%2B3?XJR&D&X!bLB(*|8Wq4-T*=PuS0zhh0I=h>Y!QJ2a8L+-H2vwnemKvQsJB z+!pa!gQ~LhuVDVK6eauh-9|OKQq0)g5g8#gi?=sIailp40?h11X9qXtw-iA9alEjh zqmuYV#yJG;bz;oGl{z?r)jA6L z8n-jknHN=3KMGhDvzH%_!!OQhXsv77oty3E*!Cw?1RNDkN8q{7**MtMBsowuds$8~JLn6ddjbx|yk}a<5CAU?*#$ zK->dm(lmTZU^d@CgUKe${O-kG3dwt@eaiSe zB;6uWXKe5lpKaA!BCV>*xU^_>Gr->Fg?cgSK~y(&l$7FEDeKqm`Pdtsn=31-4@T3G zQ|A3wEUe-L^?o#d{E^6hR46*@uY!|JS--f*fL8VjSzxe$9Qhfwc4ddd*|cZ$8fAUN zJFrLVw}3z{oPPf5#pVG2R7u=!!*R*A-RO7Uo)kiwy{vi2d>+gwEhzCpYIXnyv`pi( zs=c>H>jcPcX?;c@n>y_3aQm5xk63%43URAF>DDr-5Q2&?!)99hz^CD%*!jc4b4s7B z^@l&jSXqOjzoSO>ez@Allx>datwSin;hwN$@8Z^WBTQMrw2L}OKbS==OgTxFWY|p0 zRg+hXE7!dOgD+;TP*401Do}whSe8t3%3{0p2?Ji+Zx^(znYyl}rsbP>)OF7YBeLOE zUz2ifFVA3KLIS%!GjkjAp{3lEy%7Vx*aHP`>yHM16Z8_^CyKtO=CNfMOVvmhY? zqixbpU!ox(kkBC@5Z+($uw{3#G_y1N$0rAyhn?+?wj6|l53iAA19*IvrumhjlamfD zp0!=(I-n@CD^6RfuDqrPMP_QK=v6cdCXqZ;AM<2fVu)7^>Ezulc@^_CQJ29+tgeh4 zG@_{ErGeX951Iw6^}eYL$h;Tu{<+KRpiH46wAJk_ggEXW!bwu6i+(x2b_`<5OXZbf%6*A7&i;5DT|^ff(KuBb62{nj-&Gf@8uZ>Im}NH z)He=cIG&kEZE)3t&(!aG72$9&*OO+vwtg8?FoTzTE13k{GtTgG{T@*$RqhUa`Vg>iLeDmh1!5+ z1T*j|0rh)gx!qxc>ykUQUbUC(F&Y?bgHzHODPEZjFF1*~RZbTHHV16XSU&b9-{T{G zuj8Ys%@vZI7y#^h^yB34JU&69I(#Je)z8aqMZdcviIbV*5`EE|-k5}!*8>Z_CnGWG zm;SK^gf|Ahw>t?W1Ny#)hb0R_r_B>6d&EmrNqFIIT^Q5r7#rqTE3yi;#v&04k$DpS zCX2g9bIPU2vt-7=S}RYOO+6ZtEELzn+hL>23lHl5Iqk!KdpPp!S_)6~0r_H@eu3dq z|Kq4@DD(w>Rm@>2WS3EaYN8UAY*ZC}%4&wsWI3#+(;?qMQu%YNUuO*ZVXScGXz4aD zHcCQ$$)H38`A0AL;S4>dSVqZYZp0=mU!;Hnsu^2RG3D3O9p8DHWn1aC&BsaOeJ0&?Tn&Uc^GTct|3u8;n!9;R&^fnG5^%-s zB$@Oe%#RDeqc)globBtR-8NY+;M}Hj#M~Abbue(@MvWt!)(Lv<6W|^6>B?P*fZJnj z(ueM3jx_%};~~np-cCn;qhk1zE^e+7tJ;@YgkRB~EOY&sqE2HLl1O3H>|L%$)^`^t zIpc*e&zUVL;a5v!%mjKIlACED#fUppnLExwOUrD4P4CT^i42Ep_c+SHdZ83PcK?uG zdF0@mmdZ`THP+`CA91oRdo=9*+Z^wLB=-wKY!|b(>tqBSkzFJdV6edxJ1X2Si_KhA zil_N2rJ$LqXbW|-DWQ?|-jV@OsGGq1?LjSahmB`b&evL;2R04%FM){uYSTeE0!|IUM70}@VHKBcgQ>S zN1@bx0?&96P=K2ofoq)d%^}woH8rkROPat;x23mBh!sYsr;_j%F+)(R;VO;wwoPw07$)3xsN-!Kzl08TmV(NqV)T88~t{S zT41P&O$!NEz`iZ~h%+*=V&}PZ;8|_XuDvg}6IB0FS?`I-PsU!~%+s_pr75r#5sONM zrLgVujqp`|k!OYKBen}h6;Ng&r^773pk=#G{izptiRx^T#k1^huRj&#bX;JpeEUw7Ippx}^`nOSpPJrzIl_1?R+-JOEM$cj#oHsVa`QCy|y6cuTzkh80^*N>Syh#{`O!!)ML zqziiS#me_v4^<;>#iMU|HFhOsWz`BZeiafA7L6!I*Z_;ARrJthao;81V**z zAj@DpKy>pq^yxk~jl8Bx}dQTczj3|UIzripKkJ!7`)nto2+Lt)iV`=kFXuu0uPwqhkvZPj|urHMG=J>~e zXh-;@@^yKerllC2f{-tBo3$w^5Z=^=FKa)E*?GWdDkP4Sv_brNtk39U{K;E0#$z%%by`>`v{~5y&g(<*$Ylmd0Sgb`NM{yV8G6<^;kxA zzs{3x**PBmW)?v&Fqro6(7Rf~#PV^|rw8x1mA>k>mM;|gw%RB=VMQB+0YvE{WTC9Y z;cI2CtU@=$Z)J%i8#{M+(z-PsFWHWord$u|;{{X8@iCgDD|PT7en#%kOCx;=rgDsV zn&?ay8vK;bCE0D}tAvao1quFQ!w&b8Xd-!z0kKBruzAOT+%s&)e*JT0KM7N|obLz| zPckPIQG(CCz736kof}3ANpj#! zx;k7c|50E!Ig%L%r7#xOu6+BjGGKw95b3=mCl0zcoK&fM>Kb~KVED-pn#LAy zC|C1ThH?=bN2p={>2s$D+mJ6EEUcK{m0A&Kfh)$;y6SV3R z{=*^$+60zzTeT}o!A5t+om584h<40{jdr|g#g!zXx8o8t%!BVtguZ>KG9S$mINqM| z)0XrpM|BJ9$ur=#Ud`@i z+iBg)W%dHU%}pfxL5c@q8twi|`~bKpAg zM4w+hnkAsSRM6iC}wS8DCf7Mf(6S5Db9NJE+r4` z?`jwQwyvX{L@p*=x7WFHYtaXoRu$?7YIg#YbXV#IUYr+qM~Hh>3!QteEb^XQ4Te}4 zTj`Bg_?Jv+e!>oV22jnZ!kt8jsoex{-gXBL<}Yqh%|o^=qc2`R8ijt^vcuD4`C+|l z>HfIc63bQ&C-3Ue1{4&ftwupNzyC3*G~eB$&PEN7aK8^(%Wmh+)l>g_w%YRUyLHe~ zQLnYDOioO*w^}>+#n@I4 z|0l~+9{;-B1E8dc|M29FIww5@H-@W@?|DvOnlp56elS(!M0efq^58i|Ql;k|u!(dI z(OpnK@5>mp;{OuV!T?DF@gm=;o*?o;r7#V~WDwoRt_Js#-HXl6%&W4;oG&d0DNZ1B z^Su>2p^aR=E*AC}bvnmMRaTnnUPihB?$QC0AK!r!h}76JoY>JZl4w|4x)_Xz5-ylT z5r6y3iis`i@oobT0b$w@R)<9c%;`8!cU3kYo~yVv213tEv5{snmUkjtn-33GW>Ijc6c-rBZzKQE7FoQ_ZE zYJbB0boT*D0V+Kq(E2T~zTW3{xTIuzp1!ha?0LAZel3W9N0!r0xjg+YZM+jX4pu5T>4p#5kT)_7C0WiHe(UEEldOqE-kgr$>pxHCH;>1vL(v$Hm<1uNdY z-o9M9*|;l?xw~f6@9d+Q*oqk$xCKu3b~`I3Tl>`YY&FvNr1yc&;{{0M&{$nnDB{VL z*Qqa>!43({_puf08iVlQNNJJ<$-t|jp5^0b+Sijlb1`hUHB&+#dfHc>S#|vQV(AK! z^5%*bwdDo4sVmotG|7o`Qto)>6~gE4Vfl zIuW#N;~P$#HkIBroqoeWlMrHkJokx80|Tx{mDczK|6yThOT2HI&)cQt7ca z?VTm(jI_k~cI&sQ+!A4L^dPGOG{sTY{_ zujVhl_u+5*Xf}G>w688u-hZXIU!O9kPtkhh;JqVXPwkz26E!c1gR8@!uUL2KB-GGI zRt}w~6eKJW-P~dF&L?Yd`oR+#L+Ql_m~3~f$FUyJblv>q&hE4(nm;RA7^pntrtGv9 zx-CLB@d3yav7;%b>Xb_;H&#YG>>TDX+{>m;)`}Bf`gzikria)D-Zg)uN=Mz*#4a~i zyt~A5CATN!ne8y~eJ7AY(O4r~=`zs)C;oued$bff$wRfW4qrb-@KyA1ib`yr6u$ z#C`)wZoGUOR>MyUKXIq9yg+iw3HPS0^YqA%?1mvNMVNyAQJU{;x7^T3 zCQ$b2YfiVFw_bkQ-0ajwZH`P%!h#vh(Rs*`Av*XNkv62%Pb{oen-c%XL&$*mW{hEI z6K?5MOG9QnEkHveWkHkN$?G0!lfGv7wLHu>vYx-n(bUw-H@e!lXA6s1aeHcl&Yu{g z!tCXJd*`j@Hq#JY9&vZM*=lFlq}Dnmpdb*c>}boyZ}DZ6Zj6I&eVK8fBJy>c%;M~E zd6~Okg53$nuvW!K_q_ADKdurar?&E0G^Gf$EpiDeuduyAEe zyKf-imqCyl+RM4ZB<$1xIx_qzWi*?07qaae^3)pkAzpzoy2Q#IT{(#i_Iz*S zJ*`9qn5@nl-^dM<{JaMOT$U=&yW&Pt#erR?6Wq(Db;aXJ@l;U|$e4yc zpJJU7(4rl0zQrq?Unqy?6FPEA}f#-rc?-V zX?a-DOG+|)*G1;wJ&w;(F*z@NKm?X?Z=jN$VdTN3#4_ct@{5eJE4nLqgNd0dUS+-9 zGWp?9G$s%irgcW*rZA!nsD7}{Oww)}rmG}&jK=3to->h5B@qHG+r)`MgS4LrNa3s; zITR7t#7ROkr^(H=lXY!WEaQ}+s@j<%QaC8mHtXEy5)~AP%iya`{3rIp=@wcq30EUO z7i~PPjY#`kLi#i$BF?z*kfzs4k#1D zyS+SW3vYY#D421VN3cHdn>}=-a5E%1Go-;;#EV`eRahkIU!-JPBq(0gu1&4Ut|W`) zpB=?dU&`&xsC+paLNysIJQ<1%4x$H#5Q2lbz@ZPTgIKEL0eg$$8ZVkyepi(9$0CKw zi7J(NRlnLETM?S6g5IV`*fSk7g(>mB_WYaUJz)!l6ieK5^Sj;EqMyA=sX8Cz+dz~5 zwi@8i%efzR%yPpzGngHoAI>43Tdpwr$1PX`$X}MWk%O zr)u-z?P3X#0(K=U@$c=bH#+GeArEjU%O*IprI_ zZZm`Kw+a_Q=OXgu(?khu+QyF#BHI6h?R&2Sl-B5nHyHnu6Lsvv%ifHTkDo%`{s95- zAB_A4fy6xoxwIYtQKjLpSpKI+u%Bne#ZF7mA6|dqlrfjq@ZSUs{HKB`)FocOk@yEp z&1}D;^j|<~wGP9uMxXrkr1qNkD2}@n>lhLK#ZG|V1^a>DSGf)5{o*;j6r2i4r_mGfYJ28udip>VtP5SFi z2Ks{EN`M1Q!2T-WKhe4lB^{tViGNpJU=hC>4TFJ<EGOI<95EHSDFiT+Zg;1Fn|iIjSy~qd7K{T-#=>j?h2c*rOo7&C|dTmw+=~O z%kG}LLy|kcbV7I6PmlMO9Zxq8;DLQ=6fy-eJlUaR( z0^+`vN?!Ip`tO|(`#tlCt@UM}1EVq$MJk9N*xOk+IjYlrfWgc_3P?l+E>~fK0j$Sx!sqJS>C6g1ho&e z=l3Oteuvz|9Bsy}H+@({nrr&)^cdDGMh}ZoM}biaKK+9C0`Z+NHOzq>R3;~Egot^H z-D=o@h=2gcg?X{E0UE+`$S?4Gr&1gkX*0_)H_PE~M!!o%X`?HlU+$g5)?)LPi3obTL+M;qcI9S%)<^98l5&MWBNIjSO$Vns zTuc!WJLOX}c>{%M-x4tim|;7mJcO}9i$LMoakM(`RHNdvY5?3LoK^7Zar(jg&+N1t zTAlA=sCS_aETTx#n;+DPCnu*^vpBy^OJI6@p)Ek)cTN)9L;1+`Mcm5+*+=wz+Md4| zGrCkXQ`)<^)0Kq%*b%JgX^pBg$e?lgfz zgoDhQISt9+SitKr#68fv3@6MNq9(FZ7hs*IJz@!nBqOGotyit?%+GM-9qzi_Srw6Q zzkc-6N?Ui`be|gcbwbhI6-2JCHf&E#|-zMwAoIZO|r%QVL0EdjUP4tMPM<3zSNfcyQyc!P7kR1;;crOZ9_D$G;2wPd? zb818wz8I)%frqAwcG){X02j!28``tn>> zb-X!Zrx{jueef72|Ewrb(1OAt_nWl8sn4_IM_Tl}%qIH98N`0Xl#F4J@iC;<0v0X$ zA9YXt861InQ=U(6kWg&=%clst->N#r*zy){A;@y(enJgQJ0!DM(0`zDC5k3swp_@w zmo#V%*lE4|#T!q8V}@0XC`HV)%ww<+ zI~m2Al*+3~P^RL8XPA#h{Aj-%xwtrL>BhzLWEJi5 getFMAs(Fstr_DWeg dweg) { - val fmaAnlagen = dweg?.fmaAnlageFreimeldung.toList - if (fmaAnlagen.empty || fmaAnlagen === null) { - return emptySet - } - - if (fmaAnlagen.contains(null)) { - throw new IllegalArgumentException('''«dweg?.bezeichnung?.bezeichnungFstrDWeg?.wert» contains non-FMA-Anlagen within ID_FMA_Anlage''') - } - - val topFahrWeg = dweg?.fstrFahrweg?.topKanten - val fmaGrenzens = fmaAnlagen.map[fmaGrenzen].flatten.toSet - val startSignal = dweg.fstrFahrweg?.start - - return fmaGrenzens.filter [ fma | - val isFmaAtStartSignal = fma.singlePoints.exists [ fmaPotk | - startSignal.singlePoints.map[abstand.wert].exists [ signalDistance | - signalDistance.compareTo(fmaPotk.abstand.wert) == 0 - ] - ] - - return fma.topKanten.exists[topFahrWeg.contains(it)] && - !isFmaAtStartSignal || dweg.isRelevantFma(fma) - ].toSet - } - - // Fall start signal and the target fma stay on two different TOP_Kante - def private static boolean isRelevantFma(Fstr_DWeg dweg, Punkt_Objekt fma) { + // Check if the FMA on the Fstr_Fahrweg of the Dweg lie. + // When the Dweg ending before the free reporting section end, + // then the FMA on direction of DWeg is relevant + def static boolean isRelevantFma(Fstr_DWeg dweg, Punkt_Objekt fma, + TopPath pathToFma) { val fahrweg = dweg?.fstrFahrweg - val topFahrWeg = fahrweg?.topKanten - val startSignal = fahrweg?.start + val endFarhwegPotk = fahrweg?.zielPunktObjekt.punktObjektTOPKante val topEndFahrweg = fahrweg?.zielPunktObjekt?.topKanten - // When slip way run over a track switch - val dwegGspElement = dweg?.zuordnungen?.map[WKrGspElement] - if (!dwegGspElement.empty) { - // 1. Fall: start from leg of track switch - if (startSignal.topKanten.exists [ - dwegGspElement.map[#[topKanteL, topKanteR]].flatten.contains(it) - ]) { - return fma.topKanten.exists [ - topEndFahrweg.contains(it) - ] - } - - // 2. Fall: start from top of track switch and this switch is a combined switch - val connectionGsp = dwegGspElement.map [ - val gzL = weicheElement?.GZFreimeldungL?.element - val gzR = weicheElement?.GZFreimeldungR?.element - if (gzL !== null && topFahrWeg.contains(topKanteL)) { - return gzL - } else if (gzR !== null && topFahrWeg.contains(topKanteR)) { - return gzR - } - return null - ].filterNull - if (!connectionGsp.empty) { - return fma.topKanten.exists [ topGrenze | - connectionGsp.exists [ - gzFreimeldungTOPKante.contains(topGrenze) - ] - ] - } - } - - val fmaAnlageOnZiel = fahrweg?.container?.FMAAnlage?.filter [ - IDGleisAbschnitt?.value?.topKanten.exists [ - topEndFahrweg.contains(it) - ] && !dweg.fmaAnlageFreimeldung.contains(it) - ].toSet - return fmaAnlageOnZiel.map[fmaGrenzen].flatten.exists [ - it === fma - ] - } - private static def dispatch Set gzFreimeldungTOPKante( - Basis_Objekt basicObject) { - throw new IllegalArgumentException(basicObject.class.simpleName) - } - - private static def dispatch Set gzFreimeldungTOPKante( - W_Kr_Gsp_Element gspElement) { - return #[gspElement.topKanteL, gspElement.topKanteR].toSet + return fma.topKanten.exists[topEndFahrweg.contains(it)] || + endFarhwegPotk.exists [ + pathToFma.getDistance(new TopPoint(it)).isPresent + ] } - private static def dispatch Set gzFreimeldungTOPKante( - Gleis_Abschnitt gleisAbschnitt) { - return gleisAbschnitt.topKanten - } }