4bba57d859bdbfbee66bebdd6f33ef5fd8a5416d
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/FailIfNoValidOrcCertificateRule.java
| ... | ... | @@ -15,6 +15,7 @@ import org.junit.jupiter.api.extension.BeforeTestExecutionCallback; |
| 15 | 15 | import org.junit.jupiter.api.extension.ExtensionContext; |
| 16 | 16 | |
| 17 | 17 | import com.sap.sailing.domain.common.orc.ORCCertificate; |
| 18 | +import com.sap.sailing.domain.orc.ORCPublicCertificateDatabase.CertificateFamily; |
|
| 18 | 19 | import com.sap.sailing.domain.orc.ORCPublicCertificateDatabase.CertificateHandle; |
| 19 | 20 | import com.sap.sailing.domain.orc.ORCPublicCertificateDatabase.CountryOverview; |
| 20 | 21 | import com.sap.sailing.domain.orc.impl.ORCPublicCertificateDatabaseImpl; |
| ... | ... | @@ -52,8 +53,9 @@ public class FailIfNoValidOrcCertificateRule implements BeforeTestExecutionCallb |
| 52 | 53 | countryWithMostValidCertificates = StreamSupport |
| 53 | 54 | .stream(db.getCountriesWithValidCertificates().spliterator(), /* parallel */ false) |
| 54 | 55 | .max((c1, c2) -> c1.getCertCount() - c2.getCertCount()).get(); |
| 55 | - Iterable<CertificateHandle> certificateHandles = db.search(countryWithMostValidCertificates.getIssuingCountry(), |
|
| 56 | - countryWithMostValidCertificates.getVPPYear(), null, null, null, null, /* includeInvalid */ false); |
|
| 56 | + Iterable<CertificateHandle> certificateHandles = Util.filter(db.search(countryWithMostValidCertificates.getIssuingCountry(), |
|
| 57 | + countryWithMostValidCertificates.getVPPYear(), null, null, null, null, /* includeInvalid */ false), |
|
| 58 | + certHandle->certHandle.getFamily() != CertificateFamily.ORC_LIGHT); // exclude LITE certificates |
|
| 57 | 59 | final List<CertificateHandle> randomSubset = new ArrayList<>(); |
| 58 | 60 | Util.addAll(certificateHandles, randomSubset); |
| 59 | 61 | Collections.shuffle(randomSubset); |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCCertificateImporterJSON.java
| ... | ... | @@ -2,9 +2,11 @@ package com.sap.sailing.domain.orc; |
| 2 | 2 | |
| 3 | 3 | import static org.junit.jupiter.api.Assertions.assertNotNull; |
| 4 | 4 | import static org.junit.jupiter.api.Assertions.assertTrue; |
| 5 | +import static org.junit.jupiter.api.Assertions.fail; |
|
| 5 | 6 | |
| 6 | 7 | import java.io.IOException; |
| 7 | 8 | import java.util.Collection; |
| 9 | +import java.util.logging.Logger; |
|
| 8 | 10 | |
| 9 | 11 | import org.json.simple.parser.ParseException; |
| 10 | 12 | import org.junit.jupiter.api.Test; |
| ... | ... | @@ -13,6 +15,8 @@ import com.sap.sailing.domain.common.orc.ORCCertificate; |
| 13 | 15 | |
| 14 | 16 | |
| 15 | 17 | public class TestORCCertificateImporterJSON extends AbstractORCCertificateImporterTest { |
| 18 | + private static final Logger logger = Logger.getLogger(TestORCCertificateImporterJSON.class.getName()); |
|
| 19 | + |
|
| 16 | 20 | @Test |
| 17 | 21 | public void testSimpleLocalJSONFileRead() throws IOException, ParseException { |
| 18 | 22 | testSimpleLocalFileRead("GER2019.json", "GER20041179"); |
| ... | ... | @@ -27,9 +31,19 @@ public class TestORCCertificateImporterJSON extends AbstractORCCertificateImport |
| 27 | 31 | @Test |
| 28 | 32 | public void testSimpleOnlineFileRead() throws IOException, ParseException, InterruptedException { |
| 29 | 33 | Collection<ORCCertificate> certificates = FailIfNoValidOrcCertificateRule.getAvailableCerts(); |
| 30 | - final ORCCertificate referenceCert = certificates.stream().findFirst().get(); |
|
| 31 | - assertNotNull(referenceCert); |
|
| 32 | - assertTrue(referenceCert.getWindwardLeewardSpeedPrediction().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificate.NAUTICAL_MILE).asSeconds() > 10); |
|
| 33 | - assertTrue(referenceCert.getLongDistanceSpeedPredictions().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificate.NAUTICAL_MILE).asSeconds() > 10); |
|
| 34 | + for (final ORCCertificate referenceCert : certificates) { |
|
| 35 | + assertNotNull(referenceCert); |
|
| 36 | + // some certificates are not fully filled with allowances for all types of PCS pre-sets; we need to check whether |
|
| 37 | + // the certificate at hand has those we need for this test; else, we keep going and use another one. |
|
| 38 | + // We're already excluding LITE certificates to reduce chances for this case |
|
| 39 | + if (referenceCert.getLongDistanceSpeedPredictions().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]) != null) { |
|
| 40 | + assertTrue(referenceCert.getWindwardLeewardSpeedPrediction().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificate.NAUTICAL_MILE).asSeconds() > 10); |
|
| 41 | + assertTrue(referenceCert.getLongDistanceSpeedPredictions().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificate.NAUTICAL_MILE).asSeconds() > 10); |
|
| 42 | + return; |
|
| 43 | + } else { |
|
| 44 | + logger.info("No valid GPH found in certificate "+referenceCert.getBoatName()+" with sail number "+referenceCert.getSailNumber()); |
|
| 45 | + } |
|
| 46 | + } |
|
| 47 | + fail("Found only certificates with no valid long distance speed predictions; this seems unlikely and is probably an error"); |
|
| 34 | 48 | } |
| 35 | 49 | } |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPublicCertificateDatabase.java
| ... | ... | @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; |
| 5 | 5 | import static org.junit.jupiter.api.Assertions.assertNotNull; |
| 6 | 6 | import static org.junit.jupiter.api.Assertions.assertThrows; |
| 7 | 7 | import static org.junit.jupiter.api.Assertions.assertTrue; |
| 8 | +import static org.junit.jupiter.api.Assertions.fail; |
|
| 8 | 9 | |
| 9 | 10 | import java.time.Instant; |
| 10 | 11 | import java.time.LocalDate; |
| ... | ... | @@ -153,51 +154,59 @@ public class TestORCPublicCertificateDatabase { |
| 153 | 154 | @Test |
| 154 | 155 | public void testGetCertificate() throws Exception { |
| 155 | 156 | Collection<ORCCertificate> certificates = FailIfNoValidOrcCertificateRule.getAvailableCerts(); |
| 156 | - final ORCCertificate cert = certificates.stream().findFirst().get(); |
|
| 157 | - Iterable<CertificateHandle> certHandles = db.search(/* country */ null, LocalDate.now().getYear(), /* referenceNumber */ null, cert.getBoatName(), |
|
| 158 | - cert.getSailNumber(), /* |
|
| 159 | - * boat class name; could be set to cert.getBoatClassName() but there are |
|
| 160 | - * deviations in ORC DBs and query API, so leaving null: |
|
| 161 | - */ null, /* includeInvalid */ false); |
|
| 162 | - if (Util.isEmpty(certHandles)) { |
|
| 163 | - // there were certs; get one from the previous year |
|
| 164 | - certHandles = db.search(null, LocalDate.now().getYear()-1, null, cert.getBoatName(), |
|
| 165 | - cert.getSailNumber(), /* |
|
| 166 | - * boat class name; could be set to cert.getBoatClassName() but there are |
|
| 167 | - * deviations in ORC DBs and query API, so leaving null: |
|
| 168 | - */ null, /* includeInvalid */ false); |
|
| 169 | - } |
|
| 170 | - if (Util.isEmpty(certHandles)) { |
|
| 171 | - // there were certs; try searching by reference number |
|
| 172 | - certHandles = db.search(null, LocalDate.now().getYear(), cert.getReferenceNumber(), /* boat name may have deviated due to special characters */ null, |
|
| 173 | - cert.getSailNumber(), /* |
|
| 174 | - * boat class name; could be set to cert.getBoatClassName() but there are |
|
| 175 | - * deviations in ORC DBs and query API, so leaving null: |
|
| 176 | - */ null, /* includeInvalid */ false); |
|
| 177 | - } |
|
| 178 | - if (Util.isEmpty(certHandles)) { |
|
| 179 | - // still nothing? Then try by reference number in previous year: |
|
| 180 | - certHandles = db.search(null, LocalDate.now().getYear()-1, cert.getReferenceNumber(), /* boat name may have deviated due to special characters */ null, |
|
| 181 | - cert.getSailNumber(), /* |
|
| 182 | - * boat class name; could be set to cert.getBoatClassName() but there are |
|
| 183 | - * deviations in ORC DBs and query API, so leaving null: |
|
| 184 | - */ null, /* includeInvalid */ false); |
|
| 157 | + // some certificates seem to be lacking a GPH value; can't use those for this test; we're explicitly excluding LITE certificates already... |
|
| 158 | + for (final ORCCertificate cert : certificates) { |
|
| 159 | + if (cert.getGPH() != null) { |
|
| 160 | + Iterable<CertificateHandle> certHandles = db.search(/* country */ null, LocalDate.now().getYear(), /* referenceNumber */ null, cert.getBoatName(), |
|
| 161 | + cert.getSailNumber(), /* |
|
| 162 | + * boat class name; could be set to cert.getBoatClassName() but there are |
|
| 163 | + * deviations in ORC DBs and query API, so leaving null: |
|
| 164 | + */ null, /* includeInvalid */ false); |
|
| 165 | + if (Util.isEmpty(certHandles)) { |
|
| 166 | + // there were certs; get one from the previous year |
|
| 167 | + certHandles = db.search(null, LocalDate.now().getYear()-1, null, cert.getBoatName(), |
|
| 168 | + cert.getSailNumber(), /* |
|
| 169 | + * boat class name; could be set to cert.getBoatClassName() but there are |
|
| 170 | + * deviations in ORC DBs and query API, so leaving null: |
|
| 171 | + */ null, /* includeInvalid */ false); |
|
| 172 | + } |
|
| 173 | + if (Util.isEmpty(certHandles)) { |
|
| 174 | + // there were certs; try searching by reference number |
|
| 175 | + certHandles = db.search(null, LocalDate.now().getYear(), cert.getReferenceNumber(), /* boat name may have deviated due to special characters */ null, |
|
| 176 | + cert.getSailNumber(), /* |
|
| 177 | + * boat class name; could be set to cert.getBoatClassName() but there are |
|
| 178 | + * deviations in ORC DBs and query API, so leaving null: |
|
| 179 | + */ null, /* includeInvalid */ false); |
|
| 180 | + } |
|
| 181 | + if (Util.isEmpty(certHandles)) { |
|
| 182 | + // still nothing? Then try by reference number in previous year: |
|
| 183 | + certHandles = db.search(null, LocalDate.now().getYear()-1, cert.getReferenceNumber(), /* boat name may have deviated due to special characters */ null, |
|
| 184 | + cert.getSailNumber(), /* |
|
| 185 | + * boat class name; could be set to cert.getBoatClassName() but there are |
|
| 186 | + * deviations in ORC DBs and query API, so leaving null: |
|
| 187 | + */ null, /* includeInvalid */ false); |
|
| 188 | + } |
|
| 189 | + Optional<CertificateHandle> certificateHandle = Optional.ofNullable(certHandles.iterator().hasNext() ? certHandles.iterator().next() : null); |
|
| 190 | + assertTrue(certificateHandle.isPresent(), "No certificate found for handle "+certificateHandle+ |
|
| 191 | + " extracted from certificates "+certificates); |
|
| 192 | + final String referenceNumber = certificateHandle.get().getReferenceNumber(); |
|
| 193 | + final CertificateHandle handle = db.getCertificateHandle(referenceNumber); |
|
| 194 | + final ORCCertificate result = db.getCertificate(referenceNumber, handle.getFamily()); |
|
| 195 | + assertNotNull(result, "Unable to load certificate for reference number "+referenceNumber+" from handle "+certificateHandle); |
|
| 196 | + assertEquals(handle.getGPH(), result.getGPH().asSeconds(), 0.00001); |
|
| 197 | + // Use some tolerance as we found differences as much as 5s between the dxtDate in the handle coming from the XML search result |
|
| 198 | + // and the IssueDate field in the JSON. Both suggest to report millisecond accuracy, but dxtDate always seems to have the |
|
| 199 | + // milliseconds as "000" explaining many sub-second differences. But in some cases differences were significantly bigger. |
|
| 200 | + assertEquals(handle.getIssueDate().asMillis(), result.getIssueDate().asMillis(), 10000.0, "Issue dates of certificate with reference number "+referenceNumber+ |
|
| 201 | + " varies between current year result handle ("+handle.getIssueDate()+") and certificate ("+ |
|
| 202 | + result.getIssueDate()+")."); |
|
| 203 | + assertEquals(handle.getSailNumber(), result.getSailNumber()); |
|
| 204 | + return; |
|
| 205 | + } else { |
|
| 206 | + logger.info("No valid GPH found in certificate "+cert.getBoatName()+" with sail number "+cert.getSailNumber()); |
|
| 207 | + } |
|
| 185 | 208 | } |
| 186 | - Optional<CertificateHandle> certificateHandle = Optional.ofNullable(certHandles.iterator().hasNext() ? certHandles.iterator().next() : null); |
|
| 187 | - assertTrue(certificateHandle.isPresent(), "No certificate found for handle "+certificateHandle+ |
|
| 188 | - " extracted from certificates "+certificates); |
|
| 189 | - final String referenceNumber = certificateHandle.get().getReferenceNumber(); |
|
| 190 | - final CertificateHandle handle = db.getCertificateHandle(referenceNumber); |
|
| 191 | - final ORCCertificate result = db.getCertificate(referenceNumber, handle.getFamily()); |
|
| 192 | - assertNotNull(result, "Unable to load certificate for reference number "+referenceNumber+" from handle "+certificateHandle); |
|
| 193 | - assertEquals(handle.getGPH(), result.getGPH().asSeconds(), 0.00001); |
|
| 194 | - // Use some tolerance as we found differences as much as 5s between the dxtDate in the handle coming from the XML search result |
|
| 195 | - // and the IssueDate field in the JSON. Both suggest to report millisecond accuracy, but dxtDate always seems to have the |
|
| 196 | - // milliseconds as "000" explaining many sub-second differences. But in some cases differences were significantly bigger. |
|
| 197 | - assertEquals(handle.getIssueDate().asMillis(), result.getIssueDate().asMillis(), 10000.0, "Issue dates of certificate with reference number "+referenceNumber+ |
|
| 198 | - " varies between current year result handle ("+handle.getIssueDate()+") and certificate ("+ |
|
| 199 | - result.getIssueDate()+")."); |
|
| 200 | - assertEquals(handle.getSailNumber(), result.getSailNumber()); |
|
| 209 | + fail("No certificate found with a valid GPH; that seems very suspicious and lets this test case fail."); |
|
| 201 | 210 | } |
| 202 | 211 | |
| 203 | 212 | @FailIfNoValidOrcCertificates |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/ORCPublicCertificateDatabase.java
| ... | ... | @@ -105,7 +105,7 @@ public interface ORCPublicCertificateDatabase { |
| 105 | 105 | ORCPublicCertificateDatabase INSTANCE = new ORCPublicCertificateDatabaseImpl(); |
| 106 | 106 | |
| 107 | 107 | public enum CertificateFamily { |
| 108 | - UNKNOWN(0, ""), ORC(1, "ORC"), SUPER_YACHT(2, "SY"), DOUBLE_HANDED(3, "DH"), MULTI_HULL(4, "Mu"); |
|
| 108 | + UNKNOWN(0, ""), ORC(1, "ORC"), SUPER_YACHT(2, "SY"), DOUBLE_HANDED(3, "DH"), MULTI_HULL(4, "Mu"), NON_SPINNAKER(5, "NS"), ORC_LIGHT(6, "LITE"); |
|
| 109 | 109 | |
| 110 | 110 | private final int familyId; |
| 111 | 111 | private final String familyQueryParamValue; |
| ... | ... | @@ -137,8 +137,8 @@ public interface ORCPublicCertificateDatabase { |
| 137 | 137 | } |
| 138 | 138 | |
| 139 | 139 | /** |
| 140 | - * Data about valid certificates in a country, as obtained, e.g., from http://data.orc.org/public/WPub.dll/RMS. Such a |
|
| 141 | - * record, in its original XML representation, looks like this: |
|
| 140 | + * Data about valid certificates in a country, as obtained, e.g., from http://data.orc.org/public/WPub.dll/RMS. Such |
|
| 141 | + * a record, in its original XML representation, looks like this: |
|
| 142 | 142 | * |
| 143 | 143 | * <pre> |
| 144 | 144 | <CountryId>AUS</CountryId> |
| ... | ... | @@ -152,6 +152,87 @@ public interface ORCPublicCertificateDatabase { |
| 152 | 152 | <RMSCode>CLUB</RMSCode> |
| 153 | 153 | * </pre> |
| 154 | 154 | * |
| 155 | + * Mappings of family and type work as follows: |
|
| 156 | + * |
|
| 157 | + * Used for parsing the ORC public API (WPub.dll) RMS/JSON outputs. Note: Family 6 (ORC Light) certificates |
|
| 158 | + * generally lack GPH and Performance Curve data required for PCS calculations. |
|
| 159 | + * |
|
| 160 | + * <table border="1"> |
|
| 161 | + * <caption>ORC Family and Certificate Type Codes</caption> |
|
| 162 | + * <tr> |
|
| 163 | + * <th>Family ID</th> |
|
| 164 | + * <th>Family Name</th> |
|
| 165 | + * <th>certType</th> |
|
| 166 | + * <th>Certificate Name</th> |
|
| 167 | + * </tr> |
|
| 168 | + * <tr> |
|
| 169 | + * <td>1</td> |
|
| 170 | + * <td>ORC Standard</td> |
|
| 171 | + * <td>2</td> |
|
| 172 | + * <td>International (ORCi)</td> |
|
| 173 | + * </tr> |
|
| 174 | + * <tr> |
|
| 175 | + * <td>1</td> |
|
| 176 | + * <td>ORC Standard</td> |
|
| 177 | + * <td>3</td> |
|
| 178 | + * <td>Club</td> |
|
| 179 | + * </tr> |
|
| 180 | + * <tr> |
|
| 181 | + * <td>2</td> |
|
| 182 | + * <td>Super Yacht</td> |
|
| 183 | + * <td>7</td> |
|
| 184 | + * <td>ORCsy</td> |
|
| 185 | + * </tr> |
|
| 186 | + * <tr> |
|
| 187 | + * <td>3</td> |
|
| 188 | + * <td>Double Handed</td> |
|
| 189 | + * <td>8</td> |
|
| 190 | + * <td>DH International</td> |
|
| 191 | + * </tr> |
|
| 192 | + * <tr> |
|
| 193 | + * <td>3</td> |
|
| 194 | + * <td>Double Handed</td> |
|
| 195 | + * <td>9</td> |
|
| 196 | + * <td>DH Club</td> |
|
| 197 | + * </tr> |
|
| 198 | + * <tr> |
|
| 199 | + * <td>4</td> |
|
| 200 | + * <td>Multihull</td> |
|
| 201 | + * <td>15</td> |
|
| 202 | + * <td>Multihull International</td> |
|
| 203 | + * </tr> |
|
| 204 | + * <tr> |
|
| 205 | + * <td>4</td> |
|
| 206 | + * <td>Multihull</td> |
|
| 207 | + * <td>16</td> |
|
| 208 | + * <td>Multihull Club</td> |
|
| 209 | + * </tr> |
|
| 210 | + * <tr> |
|
| 211 | + * <td>5</td> |
|
| 212 | + * <td>Non Spinnaker</td> |
|
| 213 | + * <td>10</td> |
|
| 214 | + * <td>NS International</td> |
|
| 215 | + * </tr> |
|
| 216 | + * <tr> |
|
| 217 | + * <td>5</td> |
|
| 218 | + * <td>Non Spinnaker</td> |
|
| 219 | + * <td>11</td> |
|
| 220 | + * <td>NS Club</td> |
|
| 221 | + * </tr> |
|
| 222 | + * <tr> |
|
| 223 | + * <td>6</td> |
|
| 224 | + * <td>ORC Light</td> |
|
| 225 | + * <td>13</td> |
|
| 226 | + * <td>Light (Standard)</td> |
|
| 227 | + * </tr> |
|
| 228 | + * <tr> |
|
| 229 | + * <td>6</td> |
|
| 230 | + * <td>ORC Light</td> |
|
| 231 | + * <td>14</td> |
|
| 232 | + * <td>Light (Double Handed)</td> |
|
| 233 | + * </tr> |
|
| 234 | + * </table> |
|
| 235 | + * |
|
| 155 | 236 | * @author Axel Uhl (D043530) |
| 156 | 237 | */ |
| 157 | 238 | public interface CountryOverview { |