diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d4fd7f421..d23d98189 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,7 +1,7 @@ // For format details, see https://aka.ms/devcontainer.json. For config options, see the // README at: https://github.com/devcontainers/templates/tree/main/src/java { - "name": "WyoCV", + "name": "TIMM", // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile "image": "mcr.microsoft.com/devcontainers/java:1-21-bullseye", diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh index 8764b5930..9ab4b136e 100644 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/post-create.sh @@ -2,13 +2,13 @@ # repository for the devcontainer to build and run correctly REPO_PATH=$(dirname $(dirname $(realpath $0))) REPO_NAME=$(basename ${REPO_PATH}) -WYOCV_RES_DIR=/workspaces/${REPO_NAME}/resources +TIMM_RES_DIR=/workspaces/${REPO_NAME}/resources -echo "WYOCV_RES_DIR: ${WYOCV_RES_DIR}" +echo "TIMM_RES_DIR: ${TIMM_RES_DIR}" # Install UCP and Postgres to the local Maven repository -mvn install:install-file -Dfile="${WYOCV_RES_DIR}/ucp.jar" -DgroupId="com.oracle" -DartifactId=ucp -Dversion="12.2.0.1.0" -Dpackaging=jar -mvn install:install-file -Dfile="${WYOCV_RES_DIR}/postgresql-42.6.0.jar" -DgroupId="org.postgresql" -DartifactId=postgresql -Dversion="42.6.0" -Dpackaging=jar +mvn install:install-file -Dfile="${TIMM_RES_DIR}/ucp.jar" -DgroupId="com.oracle" -DartifactId=ucp -Dversion="12.2.0.1.0" -Dpackaging=jar +mvn install:install-file -Dfile="${TIMM_RES_DIR}/postgresql-42.6.0.jar" -DgroupId="org.postgresql" -DartifactId=postgresql -Dversion="42.6.0" -Dpackaging=jar # Install python apt update @@ -19,4 +19,4 @@ apt install -y python3 python3-pip ln -s "$(ls ~/.vscode-server/bin/* -dt | head -1)/node" /usr/local/bin # Uncomment and modify the certificate name on the following line to import a certificate into the Java keystore -# keytool -import -alias resdf -storepass changeit -noprompt -cacerts -file ${WYOCV_RES_DIR}/.cer \ No newline at end of file +# keytool -import -alias resdf -storepass changeit -noprompt -cacerts -file ${TIMM_RES_DIR}/.cer \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2ae954e60..7ea049200 100644 --- a/.gitignore +++ b/.gitignore @@ -62,7 +62,7 @@ Thumbs.db **/failsafe-summary.xml jars/*.jar -wyocv-jars-*.tar.gz +timm-jars-*.tar.gz *.bkp diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index ea9c51a74..3a3d6157b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# WyoCV Applications +# TIM Manager ![data-flow-diagram](/images/diagrams/data-flow-diagram.png) -The WyoCV Applications are a suite of tools for interacting with the Wyoming DOT ODE, with an emphasis on Traveler Information Messages (TIMs). The tool suite include modules for both sides of interaction, from the ode-wrapper used to simplify interactions with pushing TIMs, to the ode-data-logger used to subscribe to ODE Kafka topics and deposit data into a database. Each module within the project contains its own README file to help understand specific functionality. +The TIM Manager (TIMM) is a suite of tools for interacting with the Operational Data Environment (ODE), with an emphasis on Traveler Information Messages (TIMs). The tool suite include modules for both sides of interaction, from the ode-wrapper used to simplify interactions with pushing TIMs, to the ode-data-logger used to subscribe to ODE Kafka topics and deposit data into a database. Each module within the project contains its own README file to help understand specific functionality. ## Table of Contents - [Useful Links](#useful-links) @@ -46,9 +46,9 @@ These instructions will get you a copy of the project up and running on your loc git clone https://github.com/Trihydro/TIM-Manager.git ``` -2. Open the `wyocv` workspace in VS Code +2. Open the `timm` workspace in VS Code ``` - code wyocv.code-workspace + code timm.code-workspace ``` 3. Open the project in a development container @@ -93,33 +93,33 @@ To deploy the suite, first build all modules using ``` mvn clean install ``` -This will create the `target` folder under each module. From here, create a new folder structure to deploy using the `docker-compose.yml`, `.env`, and respective `.jar` file and `Dockerfile`. A basic example using the WyoCV applications as seen here follows: +This will create the `target` folder under each module. From here, create a new folder structure to deploy using the `docker-compose.yml`, `.env`, and respective `.jar` file and `Dockerfile`. A basic example using the TIMM applications as seen here follows: ``` . ├── cv-data-controller -│ ├── cv-data-controller-1.4.0-SNAPSHOT.jar +│ ├── cv-data-controller-x.x.x.jar │ ├── Dockerfile ├── cv-data-tasks -│ ├── cv-data-tasks-1.4.0-SNAPSHOT.jar +│ ├── cv-data-tasks-x.x.x.jar │ ├── Dockerfile ├── docker-compose.yml ├── ode-data-logger │ ├── Dockerfile -│ ├── ode-data-logger-1.4.0-SNAPSHOT.jar +│ ├── ode-data-logger-x.x.x.jar ├── ode-mongo-logger │ ├── Dockerfile -│ ├── ode-mongo-logger-1.4.0-SNAPSHOT.jar +│ ├── ode-mongo-logger-x.x.x.jar ├── ode-wrapper │ ├── Dockerfile -│ ├── ode-wrapper-1.4.0-SNAPSHOT.jar +│ ├── ode-wrapper-x.x.x.jar ├── ode-wrapper-docs │ └── swagger-ui-master │ ├── Dockerfile │ ├── (swagger folder structure) └── tim-refresh ├── Dockerfile - ├── tim-refresh-1.4.0-SNAPSHOT.jar + ├── tim-refresh-x.x.x.jar ``` diff --git a/cert-expiration/Dockerfile b/cert-expiration/Dockerfile index eac2f7a07..5a47ec21d 100644 --- a/cert-expiration/Dockerfile +++ b/cert-expiration/Dockerfile @@ -1,5 +1,5 @@ FROM maven:3.8-eclipse-temurin-21-alpine -ADD . /home/wyocv/wyocv_applications/cert-expiration +ADD . /home/timm/timm_applications/cert-expiration -CMD java -jar /home/wyocv/wyocv_applications/cert-expiration/cert-expiration-1.4.0-SNAPSHOT.jar +CMD java -jar /home/timm/timm_applications/cert-expiration/cert-expiration-2.0.0.jar diff --git a/cert-expiration/README.md b/cert-expiration/README.md index 495fcc0ec..2b253016c 100644 --- a/cert-expiration/README.md +++ b/cert-expiration/README.md @@ -15,7 +15,7 @@ Traveler Information Messages (TIMs) are signed by a Hardware Security Module (H These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See [deployment](#deployment) for notes on how to deploy the project on a live system. ### Docker -The following instructions are intended to be executed from the root directory of the WyoCV project: +The following instructions are intended to be executed from the root directory of the TIMM project: 1. Reopen the project in the provided dev container by clicking on the blue button in the bottom left corner of the window and selecting "Reopen in Container" 1. Open a terminal in the dev container by clicking on the `Terminal` menu and selecting `New Terminal` 1. Compile the project by running the following command: @@ -49,7 +49,7 @@ The following instructions are intended to be executed from the root directory o 1. Update the .env file with the appropriate values. See the [Configuration](#configuration) section for more information. 1. Verify that Kafka is running and accessible at the address specified in the .env file. -1. If running integration tests, verify that the CV Data Controller, WyoCV database & ODE service is running and accessible at the address specified in the .env file. +1. If running integration tests, verify that the CV Data Controller, TIMM database & ODE service is running and accessible at the address specified in the .env file. 1. Build & run the docker container with the following command: ``` docker compose up -d --build cert-expiration @@ -75,11 +75,11 @@ To run the application using the provided launch configuration: 1. Click on the `Run and Debug` button at the top of the `Run` tab 1. Select the `Cert Expiration (Launch)` configuration from the dropdown menu 1. Verify that Kafka is running and accessible at the address specified in the .env file. -1. If running integration tests, verify that the CV Data Controller, WyoCV database & ODE service is running and accessible at the address specified in the .env file. +1. If running integration tests, verify that the CV Data Controller, TIMM database & ODE service is running and accessible at the address specified in the .env file. 1. Click the green play button to start the application ## Deployment -This application is deployed using Docker, and is part of the larger WyoCVApplication suite. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. +This application is deployed using Docker, and is part of the larger TIM Manager. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. ## Configuration **SOME OF THESE PROPERTIES ARE SENSITIVE. DO NOT PUBLISH THEM TO VERSION CONTROL** diff --git a/cert-expiration/pom.xml b/cert-expiration/pom.xml index a1d7d8551..aba120f33 100644 --- a/cert-expiration/pom.xml +++ b/cert-expiration/pom.xml @@ -3,9 +3,9 @@ 4.0.0 - com.wyocv - wyo-cv - 1.4.0-SNAPSHOT + com.timm + tim-manager + 2.0.0 cert-expiration diff --git a/cv-data-controller/Dockerfile b/cv-data-controller/Dockerfile index aa1315beb..65dcf933e 100644 --- a/cv-data-controller/Dockerfile +++ b/cv-data-controller/Dockerfile @@ -1,5 +1,5 @@ FROM maven:3.8-eclipse-temurin-21-alpine -ADD . /home/wyocv/wyocv_applications/cv-data-controller +ADD . /home/timm/timm_applications/cv-data-controller -CMD java --add-opens java.base/java.lang=ALL-UNNAMED -jar /home/wyocv/wyocv_applications/cv-data-controller/cv-data-controller-1.4.0-SNAPSHOT.jar +CMD java --add-opens java.base/java.lang=ALL-UNNAMED -jar /home/timm/timm_applications/cv-data-controller/cv-data-controller-2.0.0.jar diff --git a/cv-data-controller/README.md b/cv-data-controller/README.md index ea2f77a15..c51a56132 100644 --- a/cv-data-controller/README.md +++ b/cv-data-controller/README.md @@ -1,7 +1,7 @@ # CV Data Controller ![CV Data Controller Architecture Diagram](./docs/diagrams/cv-data-controller-architecture.drawio.png) -The `cv-data-controller` module serves as the main interface for interacting with the WyoCV database and communicating with the ODE. It exposes a RESTful API, which is utilized by several modules, including ODE Wrapper, Cert Expiration, Tim Refresh, and ODE Data Logger. The API supports various HTTP methods: GET to retrieve data, PUT and POST to update data, and DELETE to remove data from the WyoCV database. Additionally, the controller includes a method for pushing data to the ODE. +The `cv-data-controller` module serves as the main interface for interacting with the TIMM database and communicating with the ODE. It exposes a RESTful API, which is utilized by several modules, including ODE Wrapper, Cert Expiration, Tim Refresh, and ODE Data Logger. The API supports various HTTP methods: GET to retrieve data, PUT and POST to update data, and DELETE to remove data from the TIMM database. Additionally, the controller includes a method for pushing data to the ODE. ## Table of Contents - [Installation](#installation) @@ -15,7 +15,7 @@ The `cv-data-controller` module serves as the main interface for interacting wit These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See [deployment](#deployment) for notes on how to deploy the project on a live system. ### Docker -The following instructions are intended to be executed from the root directory of the WyoCV project: +The following instructions are intended to be executed from the root directory of the TIMM project: 1. Reopen the project in the provided dev container by clicking on the blue button in the bottom left corner of the window and selecting "Reopen in Container" 1. Open a terminal in the dev container by clicking on the `Terminal` menu and selecting `New Terminal` 1. Compile the project by running the following command: @@ -81,7 +81,7 @@ To run the application using the provided launch configuration, follow these ste 1. Click the green play button to start the application ## Deployment -This application is deployed using Docker, and is part of the larger WyoCVApplication suite. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. +This application is deployed using Docker, and is part of the larger TIM Manager. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. ## Configuration **SOME OF THESE PROPERTIES ARE SENSITIVE. DO NOT PUBLISH THEM TO VERSION CONTROL** @@ -111,6 +111,10 @@ You may configure these values in `cv-data-controller/src/main/resources/applica | SERVER_SSL_KEY_STORE_TYPE | CONTROLLER_SERVER_SSL_KEY_STORE_TYPE | server.ssl.keyStoreType | **PROD ONLY** Keystore type (JKS) | JKS | | SERVER_SSL_KEY_ALIAS | CONTROLLER_SERVER_SSL_KEY_ALIAS | server.ssl.keyAlias | **PROD ONLY** Alias for Keystore | example.alias | +## Note on Neo4j Database +The system expects a Neo4j database to be running and accessible at the address specified in the `application.properties` file or the `sample.env` file. The +database should be populated with the necessary data for the system to function properly. The database should be running before starting the application. + ## Testing To run the unit tests, follow these steps: 1. Reopen the project in the provided dev container by clicking on the blue button in the bottom left corner of the window and selecting "Reopen in Container" diff --git a/cv-data-controller/pom.xml b/cv-data-controller/pom.xml index e0d5e9533..c3736e270 100644 --- a/cv-data-controller/pom.xml +++ b/cv-data-controller/pom.xml @@ -5,9 +5,9 @@ cv-data-controller - com.wyocv - wyo-cv - 1.4.0-SNAPSHOT + com.timm + tim-manager + 2.0.0 diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ActiveTimController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ActiveTimController.java index 1afe4c81a..a5a35e9be 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ActiveTimController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ActiveTimController.java @@ -27,6 +27,7 @@ import com.trihydro.library.model.WydotTim; import com.trihydro.library.tables.TimDbTables; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; @@ -40,6 +41,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.HttpServerErrorException; import springfox.documentation.annotations.ApiIgnore; import us.dot.its.jpo.ode.plugin.j2735.timstorage.FrameType.TravelerInfoType; @@ -47,1550 +49,1037 @@ @RestController @RequestMapping("active-tim") @ApiIgnore +@Slf4j public class ActiveTimController extends BaseController { - private TimDbTables timDbTables; - private SQLNullHandler sqlNullHandler; - protected Calendar UTCCalendar; - - public ActiveTimController() { - UTCCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - } - - @Autowired - public void InjectDependencies(TimDbTables _timDbTables, SQLNullHandler _sqlNullHandler) { - timDbTables = _timDbTables; - sqlNullHandler = _sqlNullHandler; - } - - /** - * Retrieve active TIMs that are expiring within 24 hours. - * - * Note: TIMs with a start time more than 24 hours in the future - * or an end time less than 24 hours in the future are excluded. - * - * @return List of ActiveTim objects - */ - @RequestMapping(value = "/expiring", method = RequestMethod.GET, headers = "Accept=application/json") - public ResponseEntity> GetExpiringActiveTims() { - TimUpdateModel activeTim = null; - List activeTims = new ArrayList(); - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - - try { - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - - String selectStatement = "SELECT atim.*, tt.type as tim_type_name, tt.description as tim_type_description"; - selectStatement += ", t.msg_cnt, t.url_b, t.is_satellite, t.sat_record_id, t.packet_id"; - selectStatement += ", df.data_frame_id, df.frame_type, df.duration_time, df.ssp_tim_rights, df.ssp_location_rights"; - selectStatement += ", df.ssp_msg_types, df.ssp_msg_content, df.content AS df_Content, df.url"; - selectStatement += ", r.region_id, r.anchor_lat, r.anchor_long, r.lane_width"; - selectStatement += ", r.path_id, r.closed_path, r.description AS region_description"; - selectStatement += ", r.directionality, r.direction AS region_direction"; - selectStatement += " FROM active_tim atim"; - selectStatement += " INNER JOIN tim t ON atim.tim_id = t.tim_id"; - selectStatement += " LEFT JOIN data_frame df on atim.tim_id = df.tim_id"; - selectStatement += " LEFT JOIN region r on df.data_frame_id = r.data_frame_id"; - selectStatement += " LEFT JOIN tim_type tt ON atim.tim_type_id = tt.tim_type_id"; - // where starting less than 24 hours away - selectStatement += " WHERE atim.tim_start <= (NOW() AT TIME ZONE 'UTC') + INTERVAL '24' HOUR"; - // and expiration_date within 24hrs - selectStatement += " AND (atim.expiration_date is null OR atim.expiration_date <= (NOW() AT TIME ZONE 'UTC') + INTERVAL '24' HOUR)"; - // check that end time isn't within 24hrs - selectStatement += " AND (atim.tim_end is null OR atim.tim_end >= (NOW() AT TIME ZONE 'UTC') + INTERVAL '24' HOUR)"; - - rs = statement.executeQuery(selectStatement); - - // convert to ActiveTim object - while (rs.next()) { - activeTim = new TimUpdateModel(); - - // Active_Tim properties - activeTim.setActiveTimId(rs.getLong("ACTIVE_TIM_ID")); - activeTim.setTimId(rs.getLong("TIM_ID")); - activeTim.setDirection(rs.getString("DIRECTION")); - activeTim.setStartDateTime(rs.getString("TIM_START")); - activeTim.setEndDateTime(rs.getString("TIM_END")); - activeTim.setExpirationDateTime(rs.getString("EXPIRATION_DATE")); - activeTim.setSatRecordId(rs.getString("SAT_RECORD_ID")); - activeTim.setClientId(rs.getString("CLIENT_ID")); - activeTim.setRoute(rs.getString("ROUTE")); - - Coordinate startPoint = null; - Coordinate endPoint = null; - BigDecimal startLat = rs.getBigDecimal("START_LATITUDE"); - BigDecimal startLon = rs.getBigDecimal("START_LONGITUDE"); - if (!rs.wasNull()) { - startPoint = new Coordinate(startLat, startLon); - } - activeTim.setStartPoint(startPoint); - - BigDecimal endLat = rs.getBigDecimal("END_LATITUDE"); - BigDecimal endLon = rs.getBigDecimal("END_LONGITUDE"); - if (!rs.wasNull()) { - endPoint = new Coordinate(endLat, endLon); - } - activeTim.setEndPoint(endPoint); - - activeTim.setStartDate_Timestamp(rs.getTimestamp("TIM_START", UTCCalendar)); - activeTim.setEndDate_Timestamp(rs.getTimestamp("TIM_END", UTCCalendar)); - - // Tim properties - activeTim.setMsgCnt(rs.getInt("MSG_CNT")); - activeTim.setUrlB(rs.getString("URL_B")); - activeTim.setPacketId(rs.getString("PACKET_ID")); - - // Tim Type properties - activeTim.setTimTypeName(rs.getString("TIM_TYPE_NAME")); - activeTim.setTimTypeDescription(rs.getString("TIM_TYPE_DESCRIPTION")); - - // Region Properties - activeTim.setRegionId(rs.getInt("REGION_ID")); - activeTim.setAnchorLat(rs.getBigDecimal("ANCHOR_LAT")); - activeTim.setAnchorLong(rs.getBigDecimal("ANCHOR_LONG")); - - activeTim.setLaneWidth(rs.getBigDecimal("LANE_WIDTH")); - activeTim.setRegionDirection(rs.getString("REGION_DIRECTION")); - activeTim.setDirectionality(rs.getString("DIRECTIONALITY")); - activeTim.setClosedPath(rs.getBoolean("CLOSED_PATH")); - activeTim.setPathId(rs.getInt("PATH_ID")); - activeTim.setRegionDescription(rs.getString("REGION_DESCRIPTION")); - - // DataFrame properties - activeTim.setDataFrameId(rs.getInt("DATA_FRAME_ID")); - activeTim.setDurationTime(rs.getInt("DURATION_TIME")); - activeTim.setNotUsed1((short) 0); // as of J2735 this should be set to 0 and is ignored - activeTim.setNotUsed((short) 0); // as of J2735 this should be set to 0 and is ignored - activeTim.setNotUsed3((short) 0); // as of J2735 this should be set to 0 and is ignored - activeTim.setNotUsed2((short) 0); // as of J2735 this should be set to 0 and is ignored - activeTim.setUrl(rs.getString("URL")); - - int frameTypeValue = rs.getInt("FRAME_TYPE"); - if (!rs.wasNull() && frameTypeValue >= 0 && frameTypeValue < TravelerInfoType.values().length) { - activeTim.setFrameType(TravelerInfoType.values()[frameTypeValue]); - } - - // set dataFrame content. it's required for the ODE, so if we didn't record it, - // assume Advisory - String serializedContent = rs.getString("DF_CONTENT"); - ContentEnum contentType; - if (serializedContent == null || serializedContent.isEmpty()) { - contentType = ContentEnum.advisory; - } else { - contentType = ContentEnum.fromString(serializedContent); - } - activeTim.setDfContent(contentType); - - activeTims.add(activeTim); - } - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - return ResponseEntity.ok(activeTims); - } - - @RequestMapping(value = "/update-model/{activeTimId}", method = RequestMethod.GET, headers = "Accept=application/json") - public ResponseEntity GetUpdateModelFromActiveTimId(@PathVariable Long activeTimId) { - TimUpdateModel activeTim = null; - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - - try { - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - - String selectStatement = "SELECT atim.*, tt.type AS tim_type_name, tt.description AS tim_type_description"; - selectStatement += ", t.msg_cnt, t.url_b, t.is_satellite, t.sat_record_id, t.packet_id"; - selectStatement += ", df.data_frame_id, df.frame_type, df.duration_time, df.ssp_tim_rights, df.ssp_location_rights"; - selectStatement += ", df.ssp_msg_types, df.ssp_msg_content, df.content AS df_Content, df.url"; - selectStatement += ", r.region_id, r.anchor_lat, r.anchor_long, r.lane_width"; - selectStatement += ", r.path_id, r.closed_path, r.description AS region_description"; - selectStatement += ", r.directionality, r.direction AS region_direction"; - selectStatement += " FROM active_tim atim"; - selectStatement += " INNER JOIN tim t ON atim.tim_id = t.tim_id"; - selectStatement += " LEFT JOIN data_frame df on atim.tim_id = df.tim_id"; - selectStatement += " LEFT JOIN region r on df.data_frame_id = r.data_frame_id"; - selectStatement += " LEFT JOIN tim_type tt ON atim.tim_type_id = tt.tim_type_id"; - // where active_tim_id is provided - selectStatement += " WHERE atim.active_tim_id = " + activeTimId; - rs = statement.executeQuery(selectStatement); - - // convert to ActiveTim object - while (rs.next()) { - activeTim = new TimUpdateModel(); - - // Active_Tim properties - activeTim.setActiveTimId(rs.getLong("ACTIVE_TIM_ID")); - activeTim.setTimId(rs.getLong("TIM_ID")); - activeTim.setDirection(rs.getString("DIRECTION")); - activeTim.setStartDateTime(rs.getString("TIM_START")); - activeTim.setEndDateTime(rs.getString("TIM_END")); - activeTim.setExpirationDateTime(rs.getString("EXPIRATION_DATE")); - activeTim.setSatRecordId(rs.getString("SAT_RECORD_ID")); - activeTim.setClientId(rs.getString("CLIENT_ID")); - activeTim.setRoute(rs.getString("ROUTE")); - - Coordinate startPoint = null; - Coordinate endPoint = null; - BigDecimal startLat = rs.getBigDecimal("START_LATITUDE"); - BigDecimal startLon = rs.getBigDecimal("START_LONGITUDE"); - if (!rs.wasNull()) { - startPoint = new Coordinate(startLat, startLon); - } - activeTim.setStartPoint(startPoint); - - BigDecimal endLat = rs.getBigDecimal("END_LATITUDE"); - BigDecimal endLon = rs.getBigDecimal("END_LONGITUDE"); - if (!rs.wasNull()) { - endPoint = new Coordinate(endLat, endLon); - } - activeTim.setEndPoint(endPoint); - - activeTim.setStartDate_Timestamp(rs.getTimestamp("TIM_START", UTCCalendar)); - activeTim.setEndDate_Timestamp(rs.getTimestamp("TIM_END", UTCCalendar)); - - // Tim properties - activeTim.setMsgCnt(rs.getInt("MSG_CNT")); - activeTim.setUrlB(rs.getString("URL_B")); - activeTim.setPacketId(rs.getString("PACKET_ID")); - - // Tim Type properties - activeTim.setTimTypeName(rs.getString("TIM_TYPE_NAME")); - activeTim.setTimTypeDescription(rs.getString("TIM_TYPE_DESCRIPTION")); - - // Region Properties - activeTim.setRegionId(rs.getInt("REGION_ID")); - activeTim.setAnchorLat(rs.getBigDecimal("ANCHOR_LAT")); - activeTim.setAnchorLong(rs.getBigDecimal("ANCHOR_LONG")); - - activeTim.setLaneWidth(rs.getBigDecimal("LANE_WIDTH")); - activeTim.setRegionDirection(rs.getString("REGION_DIRECTION")); - activeTim.setDirectionality(rs.getString("DIRECTIONALITY")); - activeTim.setClosedPath(rs.getBoolean("CLOSED_PATH")); - activeTim.setPathId(rs.getInt("PATH_ID")); - activeTim.setRegionDescription(rs.getString("REGION_DESCRIPTION")); - - // DataFrame properties - activeTim.setDataFrameId(rs.getInt("DATA_FRAME_ID")); - activeTim.setDurationTime(rs.getInt("DURATION_TIME")); - activeTim.setNotUsed1((short) 0); // as of J2735 2020 this should be set to 0 and is ignored - activeTim.setNotUsed((short) 0); // as of J2735 2020 this should be set to 0 and is ignored - activeTim.setNotUsed3((short) 0); // as of J2735 2020 this should be set to 0 and is ignored - activeTim.setNotUsed2((short) 0); // as of J2735 2020 this should be set to 0 and is ignored - activeTim.setUrl(rs.getString("URL")); - - int frameTypeValue = rs.getInt("FRAME_TYPE"); - if (!rs.wasNull() && frameTypeValue >= 0 && frameTypeValue < TravelerInfoType.values().length) { - activeTim.setFrameType(TravelerInfoType.values()[frameTypeValue]); + private TimDbTables timDbTables; + private SQLNullHandler sqlNullHandler; + protected Calendar UTCCalendar; + + public ActiveTimController() { + UTCCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + } + + @Autowired + public void InjectDependencies(TimDbTables _timDbTables, SQLNullHandler _sqlNullHandler) { + timDbTables = _timDbTables; + sqlNullHandler = _sqlNullHandler; + } + + /** + * Retrieve active TIMs that are expiring within 24 hours. + *

+ * Note: TIMs with a start time more than 24 hours in the future + * or an end time less than 24 hours in the future are excluded. + * + * @return List of ActiveTim objects + */ + @RequestMapping(value = "/expiring", method = RequestMethod.GET, produces = "application/json", headers = "Accept=application/json") + public ResponseEntity> GetExpiringActiveTims() { + List activeTims = new ArrayList<>(); + + String selectStatement = "SELECT atim.*, tt.type as tim_type_name, tt.description as tim_type_description"; + selectStatement += ", t.msg_cnt, t.url_b, t.is_satellite, t.sat_record_id, t.packet_id"; + selectStatement += ", df.data_frame_id, df.frame_type, df.duration_time, df.ssp_tim_rights, df.ssp_location_rights"; + selectStatement += ", df.ssp_msg_types, df.ssp_msg_content, df.content AS df_Content, df.url"; + selectStatement += ", r.region_id, r.anchor_lat, r.anchor_long, r.lane_width"; + selectStatement += ", r.path_id, r.closed_path, r.description AS region_description"; + selectStatement += ", r.directionality, r.direction AS region_direction"; + selectStatement += " FROM active_tim atim"; + selectStatement += " INNER JOIN tim t ON atim.tim_id = t.tim_id"; + selectStatement += " LEFT JOIN data_frame df on atim.tim_id = df.tim_id"; + selectStatement += " LEFT JOIN region r on df.data_frame_id = r.data_frame_id"; + selectStatement += " LEFT JOIN tim_type tt ON atim.tim_type_id = tt.tim_type_id"; + // where starting less than 24 hours away + selectStatement += " WHERE atim.tim_start <= (NOW() AT TIME ZONE 'UTC') + INTERVAL '24' HOUR"; + // and expiration_date within 24hrs + selectStatement += " AND (atim.expiration_date is null OR atim.expiration_date <= (NOW() AT TIME ZONE 'UTC') + INTERVAL '24' HOUR)"; + // check that end time isn't within 24hrs + selectStatement += " AND (atim.tim_end is null OR atim.tim_end >= (NOW() AT TIME ZONE 'UTC') + INTERVAL '24' HOUR)"; + + try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(selectStatement)) { + // convert to ActiveTim object + while (rs.next()) { + var activeTim = buildTimUpdateModelFromResultSet(rs); + + int frameTypeValue = rs.getInt("FRAME_TYPE"); + if (!rs.wasNull() && frameTypeValue >= 0 && frameTypeValue < TravelerInfoType.values().length) { + activeTim.setFrameType(TravelerInfoType.values()[frameTypeValue]); + } + else { + log.warn("Could not set frame type from value {} for active tim id {}. Assuming Advisory.", frameTypeValue, + activeTim.getActiveTimId()); + // assume advisory + activeTim.setFrameType(TravelerInfoType.advisory); } - // set dataFrame content. it's required for the ODE, so if we didn't record it, - // assume Advisory - String serializedContent = rs.getString("DF_CONTENT"); - ContentEnum contentType; - if (serializedContent == null || serializedContent.isEmpty()) { - contentType = ContentEnum.advisory; - } else { - contentType = ContentEnum.fromString(serializedContent); + // set dataFrame content. it's required for the ODE, so if we didn't record it, + // assume Advisory + String serializedContent = rs.getString("DF_CONTENT"); + ContentEnum contentType; + if (serializedContent == null || serializedContent.isEmpty()) { + contentType = ContentEnum.advisory; + } else { + contentType = ContentEnum.fromString(serializedContent); + } + activeTim.setDfContent(contentType); + + activeTims.add(activeTim); + } + } catch (Exception e) { + log.error("Error getting expiring active tims", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); + } + + return ResponseEntity.ok(activeTims); + } + + @RequestMapping(value = "/update-model/{activeTimId}", method = RequestMethod.GET, produces = "application/json", headers = "Accept=application/json") + public ResponseEntity GetUpdateModelFromActiveTimId(@PathVariable Long activeTimId) { + TimUpdateModel activeTim = null; + + String selectStatement = "SELECT atim.*, tt.type AS tim_type_name, tt.description AS tim_type_description"; + selectStatement += ", t.msg_cnt, t.url_b, t.is_satellite, t.sat_record_id, t.packet_id"; + selectStatement += ", df.data_frame_id, df.frame_type, df.duration_time, df.ssp_tim_rights, df.ssp_location_rights"; + selectStatement += ", df.ssp_msg_types, df.ssp_msg_content, df.content AS df_Content, df.url"; + selectStatement += ", r.region_id, r.anchor_lat, r.anchor_long, r.lane_width"; + selectStatement += ", r.path_id, r.closed_path, r.description AS region_description"; + selectStatement += ", r.directionality, r.direction AS region_direction"; + selectStatement += " FROM active_tim atim"; + selectStatement += " INNER JOIN tim t ON atim.tim_id = t.tim_id"; + selectStatement += " LEFT JOIN data_frame df on atim.tim_id = df.tim_id"; + selectStatement += " LEFT JOIN region r on df.data_frame_id = r.data_frame_id"; + selectStatement += " LEFT JOIN tim_type tt ON atim.tim_type_id = tt.tim_type_id"; + // where active_tim_id is provided + selectStatement += " WHERE atim.active_tim_id = " + activeTimId; + + try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(selectStatement)) { + // convert to ActiveTim object + while (rs.next()) { + // Active_Tim properties + activeTim = buildTimUpdateModelFromResultSet(rs); + + int frameTypeValue = rs.getInt("FRAME_TYPE"); + if (!rs.wasNull() && frameTypeValue >= 0 && frameTypeValue < TravelerInfoType.values().length) { + activeTim.setFrameType(TravelerInfoType.values()[frameTypeValue]); + } + else { + log.warn("Could not set frame type from value {} for active tim id {}. Assuming Advisory.", frameTypeValue, activeTimId); + // assume advisory + activeTim.setFrameType(TravelerInfoType.advisory); } - activeTim.setDfContent(contentType); - } - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTim); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - return ResponseEntity.ok(activeTim); - } - - @RequestMapping(value = "/update-sat-record-id/{activeTimId}/{satRecordId}", method = RequestMethod.PUT) - public ResponseEntity updateActiveTim_SatRecordId(@PathVariable Long activeTimId, - @PathVariable String satRecordId) { - Connection connection = null; - PreparedStatement preparedStatement = null; - List> cols = new ArrayList>(); - cols.add(new ImmutablePair("SAT_RECORD_ID", satRecordId)); - boolean success = false; - - try { - connection = dbInteractions.getConnectionPool(); - preparedStatement = timDbTables.buildUpdateStatement(activeTimId, "ACTIVE_TIM", "ACTIVE_TIM_ID", cols, - connection); - - // execute update statement - success = dbInteractions.updateOrDelete(preparedStatement); - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(false); - } finally { - try { - // close prepared statement - if (preparedStatement != null) - preparedStatement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - return ResponseEntity.ok(success); - } - - @RequestMapping(value = "/missing-itis", method = RequestMethod.GET) - public ResponseEntity> GetActiveTimsMissingItisCodes() { - List activeTims = new ArrayList(); - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - - try { - connection = dbInteractions.getConnectionPool(); - - statement = connection.createStatement(); - - // The inner subqueries leave us with a list of tim_ids that aren't associated - // with any valid itis codes. Select the active_tims with - // those tim_ids - String selectStatement = " select * from active_tim where active_tim.tim_id in"; - - // Outer subquery: Get all records that have a tim_id found to be associated - // with a null itis code (from inner subquery) - // We need to do this because there could me multiple records for a single - // tim_id - selectStatement += " (select active_tim.tim_id from active_tim"; - selectStatement += " left join data_frame on active_tim.tim_id = data_frame.tim_id"; - selectStatement += " left join data_frame_itis_code on data_frame.data_frame_id = data_frame_itis_code.data_frame_id"; - selectStatement += " where active_tim.tim_id in"; - - // Inner subquery: Get tim_ids of active_tims that _might_ not have an - // associated itis code - selectStatement += " (select active_tim.tim_id from active_tim"; - selectStatement += " left join data_frame on active_tim.tim_id = data_frame.tim_id"; - selectStatement += " left join data_frame_itis_code ON data_frame.data_frame_id = data_frame_itis_code.data_frame_id"; - selectStatement += " where data_frame_itis_code.itis_code_id is null)"; - - // Outer subquery (cont'd): Group by tim_id and filter out any records that have - // a tim_id - // associated with both null and valid itis codes (we only want tim_ids - // associated with just null itis codes) - selectStatement += " group by active_tim.tim_id"; - selectStatement += " having max(data_frame_itis_code.itis_code_id) is null)"; - - rs = statement.executeQuery(selectStatement); - - activeTims = getActiveTimFromRS(rs, false); - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - return ResponseEntity.ok(activeTims); - } - - @RequestMapping(value = "/not-sent", method = RequestMethod.GET) - public ResponseEntity> GetActiveTimsNotSent() { - List activeTims = new ArrayList(); - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - - try { - connection = dbInteractions.getConnectionPool(); - - statement = connection.createStatement(); - - String selectStatement = "select active_tim.* from active_tim"; - selectStatement += " left join tim_rsu on active_tim.tim_id = tim_rsu.tim_id"; - selectStatement += " where active_tim.sat_record_id is null"; - selectStatement += " and tim_rsu.rsu_id is null"; - rs = statement.executeQuery(selectStatement); - - activeTims = getActiveTimFromRS(rs, false); - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - return ResponseEntity.ok(activeTims); - } + // set dataFrame content. it's required for the ODE, so if we didn't record it, + // assume Advisory + String serializedContent = rs.getString("DF_CONTENT"); + ContentEnum contentType; + if (serializedContent == null || serializedContent.isEmpty()) { + contentType = ContentEnum.advisory; + } else { + contentType = ContentEnum.fromString(serializedContent); + } + activeTim.setDfContent(contentType); + } + } catch (Exception e) { + log.error("Error getting active tim", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTim); + } + + return ResponseEntity.ok(activeTim); + } + + @RequestMapping(value = "/update-sat-record-id/{activeTimId}/{satRecordId}", method = RequestMethod.PUT) + public ResponseEntity updateActiveTim_SatRecordId(@PathVariable Long activeTimId, @PathVariable String satRecordId) { + + List> cols = new ArrayList<>(); + cols.add(new ImmutablePair<>("SAT_RECORD_ID", satRecordId)); + boolean success; + + try (Connection connection = dbInteractions.getConnectionPool(); + PreparedStatement preparedStatement = timDbTables.buildUpdateStatement(activeTimId, "ACTIVE_TIM", "ACTIVE_TIM_ID", cols, connection)) { + // execute update statement + success = dbInteractions.updateOrDelete(preparedStatement); + } catch (SQLException e) { + log.error("Error updating active tim", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(false); + } + return ResponseEntity.ok(success); + } + + @RequestMapping(value = "/missing-itis", method = RequestMethod.GET) + public ResponseEntity> GetActiveTimsMissingItisCodes() { + List activeTims = new ArrayList<>(); + + // The inner subqueries leave us with a list of tim_ids that aren't associated + // with any valid itis codes. Select the active_tims with + // those tim_ids + String selectStatement = " select * from active_tim where active_tim.tim_id in"; + + // Outer subquery: Get all records that have a tim_id found to be associated + // with a null itis code (from inner subquery) + // We need to do this because there could me multiple records for a single + // tim_id + selectStatement += " (select active_tim.tim_id from active_tim"; + selectStatement += " left join data_frame on active_tim.tim_id = data_frame.tim_id"; + selectStatement += " left join data_frame_itis_code on data_frame.data_frame_id = data_frame_itis_code.data_frame_id"; + selectStatement += " where active_tim.tim_id in"; + + // Inner subquery: Get tim_ids of active_tims that _might_ not have an + // associated itis code + selectStatement += " (select active_tim.tim_id from active_tim"; + selectStatement += " left join data_frame on active_tim.tim_id = data_frame.tim_id"; + selectStatement += " left join data_frame_itis_code ON data_frame.data_frame_id = data_frame_itis_code.data_frame_id"; + selectStatement += " where data_frame_itis_code.itis_code_id is null)"; + + // Outer subquery (cont'd): Group by tim_id and filter out any records that have + // a tim_id + // associated with both null and valid itis codes (we only want tim_ids + // associated with just null itis codes) + selectStatement += " group by active_tim.tim_id"; + selectStatement += " having max(data_frame_itis_code.itis_code_id) is null)"; + + try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(selectStatement)) { + activeTims = getActiveTimFromRS(rs, false); + } catch (SQLException e) { + log.error("Error getting active tims missing itis codes", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); + } + return ResponseEntity.ok(activeTims); + } + + @RequestMapping(value = "/not-sent", method = RequestMethod.GET) + public ResponseEntity> GetActiveTimsNotSent() { + List activeTims = new ArrayList<>(); + + String selectStatement = "select active_tim.* from active_tim"; + selectStatement += " left join tim_rsu on active_tim.tim_id = tim_rsu.tim_id"; + selectStatement += " where active_tim.sat_record_id is null"; + selectStatement += " and tim_rsu.rsu_id is null"; + + try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(selectStatement)) { + activeTims = getActiveTimFromRS(rs, false); + } catch (SQLException e) { + log.error("Error getting active tims not sent", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); + } + return ResponseEntity.ok(activeTims); + } @RequestMapping(value = "/expired", method = RequestMethod.GET) - public ResponseEntity> GetExpiredActiveTims() { - List activeTims = new ArrayList(); - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - - try { - connection = dbInteractions.getConnectionPool(); - - statement = connection.createStatement(); - - String selectStatement = "select * from ACTIVE_TIM"; - selectStatement += " WHERE TIM_END <= (NOW() AT TIME ZONE 'UTC')"; - - rs = statement.executeQuery(selectStatement); - activeTims = getActiveTimFromRS(rs, false); - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - return ResponseEntity.ok(activeTims); - } - - @RequestMapping(value = "/indices-rsu/{rsuTarget}", method = RequestMethod.GET) - public ResponseEntity> GetActiveTimIndicesByRsu(@PathVariable String rsuTarget) { - - List indices = new ArrayList(); - - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - - try { - - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - - String selectStatement = "select tim_rsu.rsu_index from active_tim"; - selectStatement += " inner join tim on active_tim.tim_id = tim.tim_id"; - selectStatement += " inner join tim_rsu on tim_rsu.tim_id = tim.tim_id"; - selectStatement += " inner join rsu on rsu.rsu_id = tim_rsu.rsu_id"; - selectStatement += " inner join rsu_view on rsu.deviceid = rsu_view.deviceid"; - selectStatement += " where rsu_view.ipv4_address = '" + rsuTarget + "'"; - - rs = statement.executeQuery(selectStatement); - - // convert to ActiveTim object - while (rs.next()) { - indices.add(rs.getInt("RSU_INDEX")); - } - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(indices); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); + public ResponseEntity> GetExpiredActiveTims(@RequestParam(required = false, defaultValue = "100") Integer limit) { + String query = "SELECT * FROM ACTIVE_TIM WHERE TIM_END <= (NOW() AT TIME ZONE 'UTC') LIMIT ?"; + try (Connection connection = dbInteractions.getConnectionPool(); PreparedStatement preparedStatement = connection.prepareStatement(query)) { + preparedStatement.setInt(1, limit); + try (ResultSet rs = preparedStatement.executeQuery()) { + return ResponseEntity.ok(getActiveTimFromRS(rs, false)); } - } - - return ResponseEntity.ok(indices); - } - - @RequestMapping(value = { "/client-id-direction/{clientId}/{timTypeId}", - "/client-id-direction/{clientId}/{timTypeId}/{direction}" }, method = RequestMethod.GET) - public ResponseEntity> GetActiveTimsByClientIdDirection(@PathVariable String clientId, - @PathVariable Long timTypeId, @PathVariable(required = false) String direction) { - List activeTims = new ArrayList(); - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - - try { - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - // There may be multiple TIMs grouped together by client_id. ex. CLIENTID_1, - // CLIENTID_2 - String query = "select * from active_tim where CLIENT_ID like '" + clientId + "' and TIM_TYPE_ID = " - + timTypeId; - - if (direction != null) { - query += " and DIRECTION = '" + direction + "'"; - } - - query += " and MARKED_FOR_DELETION = '0'"; // exclude active tims marked for deletion - - rs = statement.executeQuery(query); - activeTims = getActiveTimFromRS(rs, false); - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - return ResponseEntity.ok(activeTims); - } - - @RequestMapping(value = { "/buffer-tims/{clientId}" }, method = RequestMethod.GET) - public ResponseEntity> GetBufferTimsByClientId(@PathVariable String clientId) { - List activeTims = new ArrayList(); - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - - try { - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - String query = "select * from active_tim where CLIENT_ID like '" + clientId - + "\\%BUFF_-%' ESCAPE '\\'"; - - rs = statement.executeQuery(query); - activeTims = getActiveTimFromRS(rs, false); - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - return ResponseEntity.ok(activeTims); - } - - @RequestMapping(value = "/itis-codes/{activeTimId}", method = RequestMethod.GET) - public ResponseEntity> GetItisCodesForActiveTim(@PathVariable Long activeTimId) { - List itisCodes = new ArrayList<>(); - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - - try { - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - String selectStatement = "select itis_code from active_tim "; - selectStatement += "inner join tim on tim.tim_id = active_tim.tim_id "; - selectStatement += "inner join data_frame on tim.tim_id = data_frame.tim_id "; - selectStatement += "inner join data_frame_itis_code on data_frame_itis_code.data_frame_id = data_frame.data_frame_id "; - selectStatement += "inner join itis_code on data_frame_itis_code.itis_code_id = itis_code.itis_code_id "; - selectStatement += "where active_tim_id = " + activeTimId; - selectStatement += " order by data_frame_itis_code.position asc"; - rs = statement.executeQuery(selectStatement); - - // convert to ActiveTim object - while (rs.next()) { - itisCodes.add(rs.getInt("ITIS_CODE")); - } - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(itisCodes); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - return ResponseEntity.ok(itisCodes); - } - - @RequestMapping(value = "/delete-id/{activeTimId}", method = RequestMethod.DELETE, headers = "Accept=application/json") - public ResponseEntity DeleteActiveTim(@PathVariable Long activeTimId) { - - boolean deleteActiveTimResult = false; - - String deleteSQL = "DELETE FROM ACTIVE_TIM WHERE ACTIVE_TIM_ID = ?"; - - Connection connection = null; - PreparedStatement preparedStatement = null; - - try { - - connection = dbInteractions.getConnectionPool(); - preparedStatement = connection.prepareStatement(deleteSQL); - preparedStatement.setLong(1, activeTimId); - - // execute delete SQL stetement - deleteActiveTimResult = dbInteractions.updateOrDelete(preparedStatement); - - System.out.println("Active Tim (active_tim_id " + activeTimId + ") is deleted!"); - - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(deleteActiveTimResult); - } finally { - try { - // close prepared statement - if (preparedStatement != null) - preparedStatement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - return ResponseEntity.ok(deleteActiveTimResult); - } - - @RequestMapping(value = "/delete-ids", method = RequestMethod.DELETE, headers = "Accept=application/json") - public ResponseEntity DeleteActiveTimsById(@RequestBody List activeTimIds) { - boolean deleteActiveTimResult = false; - - String deleteSQL = "DELETE FROM ACTIVE_TIM WHERE ACTIVE_TIM_ID in ("; - for (int i = 0; i < activeTimIds.size(); i++) { - deleteSQL += "?,"; - } - deleteSQL = deleteSQL.substring(0, deleteSQL.length() - 1); - deleteSQL += ")"; - - Connection connection = null; - PreparedStatement preparedStatement = null; - - try { - - connection = dbInteractions.getConnectionPool(); - preparedStatement = connection.prepareStatement(deleteSQL); - for (int i = 0; i < activeTimIds.size(); i++) { - preparedStatement.setLong(i + 1, activeTimIds.get(i)); - } - - // execute delete SQL stetement - deleteActiveTimResult = dbInteractions.updateOrDelete(preparedStatement); - - System.out.println("Active Tims (active_tim_ids " - + activeTimIds.stream().map(String::valueOf).collect(Collectors.joining(",")) + ") deleted!"); - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(deleteActiveTimResult); - } finally { - try { - // close prepared statement - if (preparedStatement != null) - preparedStatement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } + log.error("Error retrieving expired active tims", e); + throw new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR, "Error retrieving expired active tims"); } - - return ResponseEntity.ok(deleteActiveTimResult); } - @RequestMapping(value = "/get-by-ids", method = RequestMethod.POST) - public ResponseEntity> GetActiveTimsByIds(@RequestBody List ids) { - List activeTims = new ArrayList(); - if (ids == null || ids.size() == 0) { - return ResponseEntity.badRequest().body(activeTims); - } - Connection connection = null; - PreparedStatement ps = null; - ResultSet rs = null; - - try { - connection = dbInteractions.getConnectionPool(); - String query = "select * from active_tim where active_tim_id in ("; - - for (int i = 0; i < ids.size(); i++) { - query += "?, "; - } - query = query.substring(0, query.length() - 2);// subtract ', ' - query += ")"; - ps = connection.prepareStatement(query); - - for (int i = 0; i < ids.size(); i++) { - // set active_tim_id - ps.setLong(i + 1, ids.get(i)); - } - rs = ps.executeQuery(); - activeTims = getActiveTimFromRS(rs, false); - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); - } finally { - try { - // close prepared statement and result set (rs closed by prepared statement) - if (ps != null) { - ps.close(); - } - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } + @RequestMapping(value = "/indices-rsu/{rsuTarget}", method = RequestMethod.GET) + public ResponseEntity> GetActiveTimIndicesByRsu(@PathVariable String rsuTarget) { + + List indices = new ArrayList<>(); + + String selectStatement = "select tim_rsu.rsu_index from active_tim"; + selectStatement += " inner join tim on active_tim.tim_id = tim.tim_id"; + selectStatement += " inner join tim_rsu on tim_rsu.tim_id = tim.tim_id"; + selectStatement += " inner join rsu on rsu.rsu_id = tim_rsu.rsu_id"; + selectStatement += " inner join rsu_view on rsu.deviceid = rsu_view.deviceid"; + selectStatement += " where rsu_view.ipv4_address = '" + rsuTarget + "'"; + + try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(selectStatement)) { + // convert to ActiveTim object + while (rs.next()) { + indices.add(rs.getInt("RSU_INDEX")); + } + } catch (SQLException e) { + log.error("Error getting active tim indices by rsu", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(indices); + } + + return ResponseEntity.ok(indices); + } + + @RequestMapping(value = {"/client-id-direction/{clientId}/{timTypeId}", "/client-id-direction/{clientId}/{timTypeId}/{direction}"}, method = RequestMethod.GET) + public ResponseEntity> GetActiveTimsByClientIdDirection(@PathVariable String clientId, @PathVariable Long timTypeId, @PathVariable(required = false) String direction) { + List activeTims = new ArrayList<>(); + + // There may be multiple TIMs grouped together by client_id. ex. CLIENTID_1, + // CLIENTID_2 + String query = "select * from active_tim where CLIENT_ID like '" + clientId + "' and TIM_TYPE_ID = " + timTypeId; + + if (direction != null) { + query += " and DIRECTION = '" + direction + "'"; + } + + query += " and MARKED_FOR_DELETION = '0'"; // exclude active tims marked for deletion + + try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(query)) { + activeTims = getActiveTimFromRS(rs, false); + } catch (SQLException e) { + log.error("Error getting active tims by client id and direction", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); + } + + return ResponseEntity.ok(activeTims); + } + + @RequestMapping(value = {"/buffer-tims/{clientId}"}, method = RequestMethod.GET) + public ResponseEntity> GetBufferTimsByClientId(@PathVariable String clientId) { + List activeTims = new ArrayList<>(); + + String query = "select * from active_tim where CLIENT_ID like '" + clientId + "\\%BUFF_-%' ESCAPE '\\'"; + + try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(query)) { + activeTims = getActiveTimFromRS(rs, false); + } catch (SQLException e) { + log.error("Error getting buffer tims by client id", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); + } + + return ResponseEntity.ok(activeTims); + } + + @RequestMapping(value = "/itis-codes/{activeTimId}", method = RequestMethod.GET) + public ResponseEntity> GetItisCodesForActiveTim(@PathVariable Long activeTimId) { + List itisCodes = new ArrayList<>(); + + String selectStatement = "select itis_code from active_tim "; + selectStatement += "inner join tim on tim.tim_id = active_tim.tim_id "; + selectStatement += "inner join data_frame on tim.tim_id = data_frame.tim_id "; + selectStatement += "inner join data_frame_itis_code on data_frame_itis_code.data_frame_id = data_frame.data_frame_id "; + selectStatement += "inner join itis_code on data_frame_itis_code.itis_code_id = itis_code.itis_code_id "; + selectStatement += "where active_tim_id = " + activeTimId; + selectStatement += " order by data_frame_itis_code.position asc"; + + try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(selectStatement)) { + // convert to ActiveTim object + while (rs.next()) { + itisCodes.add(rs.getInt("ITIS_CODE")); + } + } catch (SQLException e) { + log.error("Error getting itis codes for active tim", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(itisCodes); + } + return ResponseEntity.ok(itisCodes); + } + + @RequestMapping(value = "/delete-id/{activeTimId}", method = RequestMethod.DELETE, headers = "Accept=application/json") + public ResponseEntity DeleteActiveTim(@PathVariable Long activeTimId) { + boolean deleteActiveTimResult = false; + + String deleteSQL = "DELETE FROM ACTIVE_TIM WHERE ACTIVE_TIM_ID = ?"; + + try (Connection connection = dbInteractions.getConnectionPool(); PreparedStatement preparedStatement = connection.prepareStatement(deleteSQL)) { + preparedStatement.setLong(1, activeTimId); + + // execute delete SQL statement + deleteActiveTimResult = dbInteractions.updateOrDelete(preparedStatement); + if (deleteActiveTimResult) { + log.info("Active Tim (active_tim_id {}) is deleted!", activeTimId); + } else { + log.warn("Failed to delete Active Tim (active_tim_id {}). It may not exist.", activeTimId); + } + + } catch (SQLException e) { + log.error("Error deleting active tim", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(deleteActiveTimResult); + } + + return ResponseEntity.ok(deleteActiveTimResult); + } + + @RequestMapping(value = "/delete-ids", method = RequestMethod.DELETE, headers = "Accept=application/json") + public ResponseEntity DeleteActiveTimsById(@RequestBody List activeTimIds) { + boolean deleteActiveTimResult = false; + + StringBuilder deleteSQL = new StringBuilder("DELETE FROM ACTIVE_TIM WHERE ACTIVE_TIM_ID in ("); + for (int i = 0; i < activeTimIds.size(); i++) { + deleteSQL.append("?,"); + } + deleteSQL = new StringBuilder(deleteSQL.substring(0, deleteSQL.length() - 1)); + deleteSQL.append(")"); + + try (Connection connection = dbInteractions.getConnectionPool(); PreparedStatement preparedStatement = connection.prepareStatement(deleteSQL.toString())) { + for (int i = 0; i < activeTimIds.size(); i++) { + preparedStatement.setLong(i + 1, activeTimIds.get(i)); + } + + // execute delete SQL statement + deleteActiveTimResult = dbInteractions.updateOrDelete(preparedStatement); + + if (deleteActiveTimResult) { + log.info("Active Tims (active_tim_ids {}) are deleted!", activeTimIds.stream().map(String::valueOf).collect(Collectors.joining(","))); + } else { + log.warn("Failed to delete Active Tims (active_tim_ids {}). They may not exist.", activeTimIds.stream().map(String::valueOf).collect(Collectors.joining(","))); + } + + } catch (SQLException e) { + log.error("Error deleting active tims", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(deleteActiveTimResult); + } + + return ResponseEntity.ok(deleteActiveTimResult); + } + + @RequestMapping(value = "/get-by-ids", method = RequestMethod.POST) + public ResponseEntity> GetActiveTimsByIds(@RequestBody List ids) { + List activeTims = new ArrayList(); + if (ids == null || ids.isEmpty()) { + return ResponseEntity.badRequest().body(activeTims); + } + + StringBuilder query = new StringBuilder("select * from active_tim where active_tim_id in ("); + + for (int i = 0; i < ids.size(); i++) { + query.append("?, "); + } + query = new StringBuilder(query.substring(0, query.length() - 2));// subtract ', ' + query.append(")"); + + try (Connection connection = dbInteractions.getConnectionPool(); PreparedStatement ps = connection.prepareStatement(query.toString())) { + for (int i = 0; i < ids.size(); i++) { + // set active_tim_id + ps.setLong(i + 1, ids.get(i)); + } + try (ResultSet rs = ps.executeQuery()) { + activeTims = getActiveTimFromRS(rs, false); + } + } catch (SQLException e) { + log.error("Error getting active tims by ids", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); + } + + return ResponseEntity.ok(activeTims); + } + + @RequestMapping(value = "/get-by-wydot-tim/{timTypeId}", method = RequestMethod.POST) + public ResponseEntity> GetActiveTimsByWydotTim(@RequestBody List wydotTims, @PathVariable Long timTypeId) { + List activeTims = new ArrayList<>(); + + WydotTim wydotTim; + + StringBuilder query = new StringBuilder("select * from active_tim where "); + if (timTypeId != null) { + query.append("TIM_TYPE_ID = ? and ("); + } + + for (int i = 0; i < wydotTims.size(); i++) { + if (i > 0) { + query.append(" OR "); + } + query.append("(CLIENT_ID like ?"); + wydotTim = wydotTims.get(i); + if (wydotTim.getDirection() != null && !wydotTim.getDirection().equalsIgnoreCase("B")) { + query.append(" and DIRECTION = ?"); + } + query.append(")"); + } + if (timTypeId != null) { + query.append(")"); + } + + try (Connection connection = dbInteractions.getConnectionPool(); PreparedStatement ps = connection.prepareStatement(query.toString())) { + + int index = 1; + if (timTypeId != null) { + ps.setLong(index, timTypeId); + index++; + } + for (WydotTim tim : wydotTims) { + wydotTim = tim; + + // set client id + ps.setString(index, wydotTim.getClientId()); + index++; + + // set direction + if (wydotTim.getDirection() != null && !wydotTim.getDirection().equalsIgnoreCase("B")) { + ps.setString(index, wydotTim.getDirection()); + index++; + } + } + try (ResultSet rs = ps.executeQuery()) { + activeTims = getActiveTimFromRS(rs, false); + } + } catch (SQLException e) { + log.error("Error getting active tims by wydot tim", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); + } + + return ResponseEntity.ok(activeTims); + } + + @RequestMapping(value = "/tim-type-id/{timTypeId}", method = RequestMethod.GET) + public ResponseEntity> GetActiveTimsByType(@PathVariable Long timTypeId) { + List activeTims = new ArrayList(); + + try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); + ResultSet rs = statement.executeQuery("select * from active_tim where TIM_TYPE_ID = " + timTypeId)) { + activeTims = getActiveTimFromRS(rs, false); + } catch (SQLException e) { + log.error("Error getting active tims by type", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); + } + + return ResponseEntity.ok(activeTims); + } + + @RequestMapping(value = "/all", method = RequestMethod.GET) + public ResponseEntity> GetAllActiveTims() { + List activeTims = new ArrayList<>(); + + String selectStatement = "select * from active_tim"; + + try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(selectStatement)) { + activeTims = getActiveTimFromRS(rs, false); + } catch (SQLException e) { + log.error("Error getting all active tims", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); + } + + return ResponseEntity.ok(activeTims); + } + + @RequestMapping(value = "/all-sdx", method = RequestMethod.GET) + public ResponseEntity> GetAllActiveSDXTims() { + return getActiveTimsWithItisCodes(true, false); + } + + @RequestMapping(value = "/all-with-itis", method = RequestMethod.GET) + public ResponseEntity> GetAllActiveTimsWithItis(@RequestParam(required = false) Boolean excludeVslAndParking) { + // Configure default value + if (excludeVslAndParking == null) { + excludeVslAndParking = false; + } + + return getActiveTimsWithItisCodes(false, excludeVslAndParking); + } + + private ResponseEntity> getActiveTimsWithItisCodes(boolean sdxOnly, boolean excludeVslAndParking) { + List results = new ArrayList(); + ActiveTim activeTim = null; + + String query = "select active_tim.*, tim_type.type, itis_code.itis_code from active_tim"; + query += " left join tim_type on active_tim.tim_type_id = tim_type.tim_type_id"; + query += " left join data_frame on active_tim.tim_id = data_frame.tim_id"; + query += " left join data_frame_itis_code on data_frame.data_frame_id = data_frame_itis_code.data_frame_id"; + query += " left join itis_code on data_frame_itis_code.itis_code_id = itis_code.itis_code_id"; + + if (sdxOnly) { + query += " where sat_record_id is not null"; + } + + if (excludeVslAndParking) { + if (query.contains("where")) { + query += " and tim_type.type not in ('P', 'VSL')"; + } else { + query += " where tim_type.type not in ('P', 'VSL')"; + } + } + + query += " order by active_tim.active_tim_id, data_frame_itis_code.position asc"; + + try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(query)) { + // convert to ActiveTim object + while (rs.next()) { + Long activeTimId = rs.getLong("ACTIVE_TIM_ID"); + + // If we're looking at the first record or the record doesn't have + // the same ACTIVE_TIM_ID as the record we just processed... + if (activeTim == null || !activeTim.getActiveTimId().equals(activeTimId)) { + if (activeTim != null) { + results.add(activeTim); + } + + // Create a new record and set the ActiveTim properties. + activeTim = new ActiveTim(); + activeTim.setActiveTimId(activeTimId); + activeTim.setTimId(rs.getLong("TIM_ID")); + activeTim.setDirection(rs.getString("DIRECTION")); + activeTim.setStartDateTime(rs.getString("TIM_START")); + activeTim.setEndDateTime(rs.getString("TIM_END")); + activeTim.setExpirationDateTime(rs.getString("EXPIRATION_DATE")); + activeTim.setRoute(rs.getString("ROUTE")); + activeTim.setClientId(rs.getString("CLIENT_ID")); + activeTim.setSatRecordId(rs.getString("SAT_RECORD_ID")); + activeTim.setPk(rs.getInt("PK")); + activeTim.setItisCodes(new ArrayList()); + + Coordinate startPoint = null; + Coordinate endPoint = null; + + // Set startPoint + BigDecimal startLat = rs.getBigDecimal("START_LATITUDE"); + BigDecimal startLon = rs.getBigDecimal("START_LONGITUDE"); + if (!rs.wasNull()) { + startPoint = new Coordinate(startLat, startLon); + } + activeTim.setStartPoint(startPoint); + + // Set endPoint + BigDecimal endLat = rs.getBigDecimal("END_LATITUDE"); + BigDecimal endLon = rs.getBigDecimal("END_LONGITUDE"); + if (!rs.wasNull()) { + endPoint = new Coordinate(endLat, endLon); + } + activeTim.setEndPoint(endPoint); + + // Set timType + long timTypeId = rs.getLong("TIM_TYPE_ID"); + if (!rs.wasNull()) { + activeTim.setTimTypeId(timTypeId); + activeTim.setTimType(rs.getString("TYPE")); + } + + // Set projectKey + int projectKey = rs.getInt("PROJECT_KEY"); + if (!rs.wasNull()) { + activeTim.setProjectKey(projectKey); + } + } + + // Add the ITIS code to the ActiveTim's ITIS codes, if not null + var itisCode = rs.getInt("ITIS_CODE"); + if (!rs.wasNull()) { + activeTim.getItisCodes().add(itisCode); + } + } + + if (activeTim != null) { + results.add(activeTim); + } + } catch (SQLException e) { + log.error("Error getting active tims with itis codes", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); + } + + return ResponseEntity.ok(results); + } + + /** + * Get all ActiveTims (including RSU address and RSU Index) + * + * @return List of ActiveTims, sorted by RSU and RSU Index + */ + @RequestMapping(value = "/active-rsu-tims", method = RequestMethod.GET) + public ResponseEntity> GetActiveRsuTims() { + List results = new ArrayList(); + ActiveTim activeTim = null; + + String query = "select active_tim.*, rsu_view.ipv4_address, tim_rsu.rsu_index from active_tim"; + query += " inner join tim_rsu on active_tim.tim_id = tim_rsu.tim_id"; + query += " inner join rsu on tim_rsu.rsu_id = rsu.rsu_id"; + query += " inner join rsu_view on rsu.deviceid = rsu_view.deviceid"; + query += " where sat_record_id is null"; + query += " order by rsu_view.ipv4_address, tim_rsu.rsu_index"; // Required by ValidateRsus + + try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(query)) { + // convert to ActiveTim object + while (rs.next()) { + // Create ActiveTim record + activeTim = new ActiveTim(); + activeTim.setActiveTimId(rs.getLong("ACTIVE_TIM_ID")); + activeTim.setTimId(rs.getLong("TIM_ID")); + activeTim.setDirection(rs.getString("DIRECTION")); + activeTim.setStartDateTime(rs.getString("TIM_START")); + activeTim.setEndDateTime(rs.getString("TIM_END")); + activeTim.setExpirationDateTime(rs.getString("EXPIRATION_DATE")); + activeTim.setTimTypeId(rs.getLong("TIM_TYPE_ID")); + activeTim.setRoute(rs.getString("ROUTE")); + activeTim.setClientId(rs.getString("CLIENT_ID")); + activeTim.setSatRecordId(rs.getString("SAT_RECORD_ID")); + activeTim.setPk(rs.getInt("PK")); + activeTim.setRsuTarget(rs.getString("IPV4_ADDRESS")); + activeTim.setRsuIndex(rs.getInt("RSU_INDEX")); + Coordinate startPoint = null; + Coordinate endPoint = null; + BigDecimal startLat = rs.getBigDecimal("START_LATITUDE"); + BigDecimal startLon = rs.getBigDecimal("START_LONGITUDE"); + if (!rs.wasNull()) { + startPoint = new Coordinate(startLat, startLon); + } + activeTim.setStartPoint(startPoint); + + BigDecimal endLat = rs.getBigDecimal("END_LATITUDE"); + BigDecimal endLon = rs.getBigDecimal("END_LONGITUDE"); + if (!rs.wasNull()) { + endPoint = new Coordinate(endLat, endLon); + } + activeTim.setEndPoint(endPoint); + results.add(activeTim); + } + } catch (SQLException e) { + log.error("Error getting active tims with rsu", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); + } + + return ResponseEntity.ok(results); + } + + @RequestMapping(value = "/active-rsu-tim", method = RequestMethod.POST) + public ResponseEntity GetActiveRsuTim(@RequestBody ActiveRsuTimQueryModel artqm) { + + ActiveTim activeTim = null; + + String query = "select * from active_tim"; + query += " inner join tim_rsu on active_tim.tim_id = tim_rsu.tim_id"; + query += " inner join rsu on tim_rsu.rsu_id = rsu.rsu_id"; + query += " inner join rsu_view on rsu.deviceid = rsu_view.deviceid"; + query += " where ipv4_address = '" + artqm.getIpv4() + "' and client_id = '" + artqm.getClientId() + "' and active_tim.direction = '" + artqm.getDirection() + "'"; + + try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(query)) { + List activeTims = getActiveTimFromRS(rs, false); + if (!activeTims.isEmpty()) { + activeTim = activeTims.get(activeTims.size() - 1); + } + } catch (SQLException e) { + log.error("Error getting active tims with rsu", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTim); + } + + return ResponseEntity.ok(activeTim); + } + + @RequestMapping(value = "/add", method = RequestMethod.POST) + public ResponseEntity InsertActiveTim(@RequestBody ActiveTim activeTim) { + Long activeTimId = 0L; + + String insertQueryStatement = timDbTables.buildInsertQueryStatement("active_tim", timDbTables.getActiveTimTable()); + + try (Connection connection = dbInteractions.getConnectionPool(); PreparedStatement preparedStatement = connection.prepareStatement(insertQueryStatement, new String[] {"active_tim_id"})) { + int fieldNum = 1; + + for (String col : timDbTables.getActiveTimTable()) { + if (col.equals("TIM_ID")) { + sqlNullHandler.setLongOrNull(preparedStatement, fieldNum, activeTim.getTimId()); + } else if (col.equals("DIRECTION")) { + sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, activeTim.getDirection()); + } else if (col.equals("TIM_START")) { + java.util.Date tim_start_date = utility.convertDate(activeTim.getStartDateTime()); + Timestamp tim_start_timestamp = new Timestamp(tim_start_date.getTime()); + sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, tim_start_timestamp); + } else if (col.equals("TIM_END")) { + if (activeTim.getEndDateTime() != null) { + java.util.Date tim_end_date = utility.convertDate(activeTim.getEndDateTime()); + Timestamp tim_end_timestamp = new Timestamp(tim_end_date.getTime()); + sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, tim_end_timestamp); + } else { + preparedStatement.setNull(fieldNum, java.sql.Types.TIMESTAMP); + } + } else if (col.equals("EXPIRATION_DATE")) { + if (activeTim.getExpirationDateTime() != null) { + java.util.Date tim_exp_date = utility.convertDate(activeTim.getExpirationDateTime()); + Timestamp tim_exp_timestamp = new Timestamp(tim_exp_date.getTime()); + sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, tim_exp_timestamp); + } else { + preparedStatement.setNull(fieldNum, java.sql.Types.TIMESTAMP); + } + } else if (col.equals("TIM_TYPE_ID")) { + sqlNullHandler.setLongOrNull(preparedStatement, fieldNum, activeTim.getTimTypeId()); + } else if (col.equals("ROUTE")) { + sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, activeTim.getRoute()); + } else if (col.equals("CLIENT_ID")) { + sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, activeTim.getClientId()); + } else if (col.equals("SAT_RECORD_ID")) { + sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, activeTim.getSatRecordId()); + } else if (col.equals("PK")) { + sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, activeTim.getPk()); + } else if (col.equals("START_LATITUDE")) { + BigDecimal start_lat = null; + if (activeTim.getStartPoint() != null) { + start_lat = activeTim.getStartPoint().getLatitude(); + } + sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, start_lat); + } else if (col.equals("START_LONGITUDE")) { + BigDecimal start_lon = null; + if (activeTim.getStartPoint() != null) { + start_lon = activeTim.getStartPoint().getLongitude(); + } + sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, start_lon); + } else if (col.equals("END_LATITUDE")) { + BigDecimal end_lat = null; + if (activeTim.getEndPoint() != null) { + end_lat = activeTim.getEndPoint().getLatitude(); + } + sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, end_lat); + } else if (col.equals("END_LONGITUDE")) { + BigDecimal end_lon = null; + if (activeTim.getEndPoint() != null) { + end_lon = activeTim.getEndPoint().getLongitude(); + } + sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, end_lon); + } else if (col.equals("PROJECT_KEY")) { + sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, activeTim.getProjectKey()); + } + + fieldNum++; + } + + activeTimId = dbInteractions.executeAndLog(preparedStatement, "active tim"); + return ResponseEntity.ok(activeTimId); + } catch (SQLException e) { + log.error("Error inserting active tim", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTimId); + } + } + + private List getActiveTimFromRS(ResultSet rs, boolean includeType) throws SQLException { + List activeTims = new ArrayList<>(); + ActiveTim activeTim; + + // convert to ActiveTim object + while (rs.next()) { + activeTim = new ActiveTim(); + activeTim.setActiveTimId(rs.getLong("ACTIVE_TIM_ID")); + activeTim.setTimId(rs.getLong("TIM_ID")); + activeTim.setSatRecordId(rs.getString("SAT_RECORD_ID")); + activeTim.setClientId(rs.getString("CLIENT_ID")); + activeTim.setDirection(rs.getString("DIRECTION")); + activeTim.setEndDateTime(rs.getString("TIM_END")); + activeTim.setStartDateTime(rs.getString("TIM_START")); + activeTim.setExpirationDateTime(rs.getString("EXPIRATION_DATE")); + activeTim.setRoute(rs.getString("ROUTE")); + activeTim.setPk(rs.getInt("PK")); + activeTim.setTimTypeId(rs.getLong("TIM_TYPE_ID")); + + if (includeType) { + activeTim.setTimType(rs.getString("TYPE")); + } + + Coordinate startPoint = null; + Coordinate endPoint = null; + BigDecimal startLat = rs.getBigDecimal("START_LATITUDE"); + BigDecimal startLon = rs.getBigDecimal("START_LONGITUDE"); + if (!rs.wasNull()) { + startPoint = new Coordinate(startLat, startLon); + } + activeTim.setStartPoint(startPoint); + + BigDecimal endLat = rs.getBigDecimal("END_LATITUDE"); + BigDecimal endLon = rs.getBigDecimal("END_LONGITUDE"); + if (!rs.wasNull()) { + endPoint = new Coordinate(endLat, endLon); + } + activeTim.setEndPoint(endPoint); + + activeTims.add(activeTim); + } + + return activeTims; + } + + private String translateIso8601ToTimestampFormat(String date) throws ParseException { + DateFormat sdf = new SimpleDateFormat("dd-MMM-yy hh.mm.ss.SSS a"); + // TimeZone toTimeZone = TimeZone.getTimeZone("MST"); + // sdf.setTimeZone(toTimeZone); + DateFormat m_ISO8601Local = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + Date dte = m_ISO8601Local.parse(date); + return sdf.format(dte.getTime()); + } + + private List> SplitMaxList(List activeTimIds, int maxListSize) { + Long[] elements = activeTimIds.toArray(new Long[0]); + int maxChunks = (int) Math.ceil(elements.length / (double) maxListSize); + List> returnValues = new ArrayList>(maxChunks); + + for (int i = 0; i < maxChunks; i++) { + int from = i * maxListSize; + int to = Math.min(from + maxListSize, elements.length); + Long[] range = Arrays.copyOfRange(elements, from, to); + returnValues.add(Arrays.asList(range)); + } + return returnValues; + } + + @RequestMapping(value = "/reset-expiration-date", method = RequestMethod.PUT, produces = "application/json", headers = "Accept=application/json") + public ResponseEntity ResetExpirationDate(@RequestBody List activeTimIds) { + if (activeTimIds == null || activeTimIds.isEmpty()) { + return ResponseEntity.ok(true); + } + + boolean result = true; + // on occasion we have over 1000 active tim ids, resulting in error + // split this out by 500 records at a time to avoid issues + List> splitActiveTims = SplitMaxList(activeTimIds, 500); + + try (Connection connection = dbInteractions.getConnectionPool()) { + + for (int splitTimsIndex = 0; splitTimsIndex < splitActiveTims.size(); splitTimsIndex++) { + List splitTims = splitActiveTims.get(splitTimsIndex); + StringBuilder updateSql = new StringBuilder("UPDATE ACTIVE_TIM SET EXPIRATION_DATE = NULL WHERE ACTIVE_TIM_ID IN ("); + + for (int i = 0; i < splitTims.size(); i++) { + updateSql.append("?,"); + } + updateSql = new StringBuilder(updateSql.substring(0, updateSql.length() - 1)); + updateSql.append(")"); + + try (PreparedStatement preparedStatement = connection.prepareStatement(updateSql.toString())) { + for (int i = 0; i < splitTims.size(); i++) { + preparedStatement.setLong(i + 1, splitTims.get(i)); + } + + // execute delete SQL statement + result &= dbInteractions.updateOrDelete(preparedStatement); + } catch (SQLException e) { + log.error("Error resetting expiration date for active tims", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(false); + } + } + + log.info("Reset expiration date for Active Tims (active_tim_ids {})", activeTimIds.stream().map(String::valueOf).collect(Collectors.joining(","))); + + } catch (SQLException e) { + log.error("Error resetting expiration date for active tims", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(false); + } + + return ResponseEntity.ok(result); + } + + @RequestMapping(value = "/update-expiration/{packetID}/{expDate}", method = RequestMethod.PUT) + public ResponseEntity UpdateExpiration(@PathVariable String packetID, @PathVariable String expDate) { + boolean success; + + String query = "SELECT ACTIVE_TIM_ID FROM ACTIVE_TIM atim"; + query += " INNER JOIN TIM ON atim.TIM_ID = TIM.TIM_ID"; + query += " WHERE TIM.PACKET_ID = ?"; + + String updateStatement = "UPDATE ACTIVE_TIM SET EXPIRATION_DATE = ? WHERE ACTIVE_TIM_ID IN ("; + updateStatement += query; + updateStatement += ")"; + + try (Connection connection = dbInteractions.getConnectionPool(); PreparedStatement preparedStatement = connection.prepareStatement(updateStatement)) { + Date date = utility.convertDate(expDate); + Timestamp expDateTimestamp = new Timestamp(date.getTime()); + preparedStatement.setTimestamp(1, expDateTimestamp);// expDate comes in as MST from previously called function + // (GetMinExpiration) + preparedStatement.setObject(2, packetID); + + // execute update statement + success = dbInteractions.updateOrDelete(preparedStatement); + } catch (Exception e) { + log.error("Error updating expiration date for packetID: {}, expDate: {}", packetID, expDate, e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(false); + } + log.info("Called UpdateExpiration with packetID: {}, expDate: {}. Successful: {}", packetID, expDate, success); + return ResponseEntity.ok(success); + } + + @RequestMapping(value = "/get-min-expiration/{packetID}/{expDate}") + public ResponseEntity GetMinExpiration(@PathVariable String packetID, @PathVariable String expDate) throws ParseException { + String minStart = ""; + + // Fetch the minimum of passed in expDate and database held + // active_tim.expiration_date. To compare like values we convert the expDate + // TO_TIMESTAMP. Without this it compares string length. + // Also, there are some null values in the db. To get around these, we use the + // coalesce function with the expDate passed in value. + String targetFormat = "DD-MON-YYYY HH12.MI.SS a"; + String selectTimestamp = String.format("SELECT TO_TIMESTAMP('%s', '%s')", translateIso8601ToTimestampFormat(expDate), targetFormat); + + + String minExpDate = "SELECT MIN(EXPIRATION_DATE) FROM ACTIVE_TIM atim"; + minExpDate += " INNER JOIN TIM ON atim.TIM_ID = TIM.TIM_ID"; + minExpDate += " WHERE TIM.PACKET_ID = '" + packetID + "'"; + + String query = String.format("SELECT LEAST((%s), (COALESCE((%s),(%s)))) minStart", selectTimestamp, minExpDate, selectTimestamp); + + try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(query)) { + while (rs.next()) { + var tmpTs = rs.getTimestamp("MINSTART", UTCCalendar); + minStart = utility.timestampFormat.format(tmpTs); + } + } catch (SQLException e) { + log.error("Error getting min expiration date for packetID: {}, expDate: {}", packetID, expDate, e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(minStart); + } + log.info("Called GetMinExpiration with packetID: {}, expDate: {}. Min start date: {}", packetID, expDate, minStart); + return ResponseEntity.ok(minStart); + } + + @RequestMapping(value = "/mark-for-deletion/{activeTimId}", method = RequestMethod.PUT) + public ResponseEntity MarkForDeletion(@PathVariable Long activeTimId) { + boolean success; + + String updateStatement = "UPDATE ACTIVE_TIM SET MARKED_FOR_DELETION = '1' WHERE ACTIVE_TIM_ID = ?"; + + try (Connection connection = dbInteractions.getConnectionPool(); PreparedStatement preparedStatement = connection.prepareStatement(updateStatement)) { + preparedStatement.setLong(1, activeTimId); + + // execute update statement + success = dbInteractions.updateOrDelete(preparedStatement); + } catch (Exception e) { + log.error("Error marking active tim for deletion with activeTimId: {}", activeTimId, e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(false); + } + if (!success) { + log.warn("Failed to mark active tim for deletion with activeTimId: {}", activeTimId); + } + return ResponseEntity.ok(success); + } + + private TimUpdateModel buildTimUpdateModelFromResultSet(ResultSet rs) throws SQLException { + TimUpdateModel timUpdateModel = new TimUpdateModel(); + timUpdateModel.setActiveTimId(rs.getLong("ACTIVE_TIM_ID")); + timUpdateModel.setTimId(rs.getLong("TIM_ID")); + timUpdateModel.setDirection(rs.getString("DIRECTION")); + timUpdateModel.setStartDateTime(rs.getString("TIM_START")); + timUpdateModel.setEndDateTime(rs.getString("TIM_END")); + timUpdateModel.setExpirationDateTime(rs.getString("EXPIRATION_DATE")); + timUpdateModel.setSatRecordId(rs.getString("SAT_RECORD_ID")); + timUpdateModel.setClientId(rs.getString("CLIENT_ID")); + timUpdateModel.setRoute(rs.getString("ROUTE")); + + Coordinate startPoint = null; + Coordinate endPoint = null; + BigDecimal startLat = rs.getBigDecimal("START_LATITUDE"); + BigDecimal startLon = rs.getBigDecimal("START_LONGITUDE"); + if (!rs.wasNull()) { + startPoint = new Coordinate(startLat, startLon); } + timUpdateModel.setStartPoint(startPoint); - return ResponseEntity.ok(activeTims); - } - - @RequestMapping(value = "/get-by-wydot-tim/{timTypeId}", method = RequestMethod.POST) - public ResponseEntity> GetActiveTimsByWydotTim(@RequestBody List wydotTims, - @PathVariable Long timTypeId) { - List activeTims = new ArrayList(); - Connection connection = null; - PreparedStatement ps = null; - ResultSet rs = null; - WydotTim wydotTim = null; - - try { - connection = dbInteractions.getConnectionPool(); - String query = "select * from active_tim where "; - if (timTypeId != null) { - query += "TIM_TYPE_ID = ? and ("; - } - - for (int i = 0; i < wydotTims.size(); i++) { - if (i > 0) { - query += " OR "; - } - query += "(CLIENT_ID like ?"; - wydotTim = wydotTims.get(i); - if (wydotTim.getDirection() != null && !wydotTim.getDirection().equalsIgnoreCase("B")) { - query += " and DIRECTION = ?"; - } - query += ")"; - } - if (timTypeId != null) { - query += ")"; - } - ps = connection.prepareStatement(query); - - int index = 1; - if (timTypeId != null) { - ps.setLong(index, timTypeId); - index++; - } - for (int i = 0; i < wydotTims.size(); i++) { - wydotTim = wydotTims.get(i); - - // set client id - ps.setString(index, wydotTim.getClientId()); - index++; - - // set direction - if (wydotTim.getDirection() != null && !wydotTim.getDirection().equalsIgnoreCase("B")) { - ps.setString(index, wydotTim.getDirection()); - index++; - } - } - rs = ps.executeQuery(); - activeTims = getActiveTimFromRS(rs, false); - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); - } finally { - try { - // close prepared statement and result set (rs closed by prepared statement) - if (ps != null) { - ps.close(); - } - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - return ResponseEntity.ok(activeTims); - } - - @RequestMapping(value = "/tim-type-id/{timTypeId}", method = RequestMethod.GET) - public ResponseEntity> GetActiveTimsByType(@PathVariable Long timTypeId) { - List activeTims = new ArrayList(); - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - - try { - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - rs = statement.executeQuery("select * from active_tim where TIM_TYPE_ID = " + timTypeId); - activeTims = getActiveTimFromRS(rs, false); - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - return ResponseEntity.ok(activeTims); - } - - @RequestMapping(value = "/all", method = RequestMethod.GET) - public ResponseEntity> GetAllActiveTims() { - List activeTims = new ArrayList(); - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - - try { - connection = dbInteractions.getConnectionPool(); - - statement = connection.createStatement(); - - String selectStatement = "select * from active_tim"; - - rs = statement.executeQuery(selectStatement); - activeTims = getActiveTimFromRS(rs, false); - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTims); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - return ResponseEntity.ok(activeTims); - } - - @RequestMapping(value = "/all-sdx", method = RequestMethod.GET) - public ResponseEntity> GetAllActiveSDXTims() { - return getActiveTimsWithItisCodes(true, false); - } - - @RequestMapping(value = "/all-with-itis", method = RequestMethod.GET) - public ResponseEntity> GetAllActiveTimsWithItis( - @RequestParam(required = false) Boolean excludeVslAndParking) { - // Configure default value - if (excludeVslAndParking == null) { - excludeVslAndParking = false; - } - - return getActiveTimsWithItisCodes(false, excludeVslAndParking); - } - - private ResponseEntity> getActiveTimsWithItisCodes(boolean sdxOnly, boolean excludeVslAndParking) { - List results = new ArrayList(); - ActiveTim activeTim = null; - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - - try { - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - - String query = "select active_tim.*, tim_type.type, itis_code.itis_code from active_tim"; - query += " left join tim_type on active_tim.tim_type_id = tim_type.tim_type_id"; - query += " left join data_frame on active_tim.tim_id = data_frame.tim_id"; - query += " left join data_frame_itis_code on data_frame.data_frame_id = data_frame_itis_code.data_frame_id"; - query += " left join itis_code on data_frame_itis_code.itis_code_id = itis_code.itis_code_id"; - - if (sdxOnly) { - query += " where sat_record_id is not null"; - } - - if (excludeVslAndParking) { - if (query.contains("where")) { - query += " and tim_type.type not in ('P', 'VSL')"; - } else { - query += " where tim_type.type not in ('P', 'VSL')"; - } - } - - query += " order by active_tim.active_tim_id, data_frame_itis_code.position asc"; - - rs = statement.executeQuery(query); - - // convert to ActiveTim object - while (rs.next()) { - Long activeTimId = rs.getLong("ACTIVE_TIM_ID"); - - // If we're looking at the first record or the record doesn't have - // the same ACTIVE_TIM_ID as the record we just processed... - if (activeTim == null || !activeTim.getActiveTimId().equals(activeTimId)) { - if (activeTim != null) { - results.add(activeTim); - } - - // Create a new record and set the ActiveTim properties. - activeTim = new ActiveTim(); - activeTim.setActiveTimId(activeTimId); - activeTim.setTimId(rs.getLong("TIM_ID")); - activeTim.setDirection(rs.getString("DIRECTION")); - activeTim.setStartDateTime(rs.getString("TIM_START")); - activeTim.setEndDateTime(rs.getString("TIM_END")); - activeTim.setExpirationDateTime(rs.getString("EXPIRATION_DATE")); - activeTim.setRoute(rs.getString("ROUTE")); - activeTim.setClientId(rs.getString("CLIENT_ID")); - activeTim.setSatRecordId(rs.getString("SAT_RECORD_ID")); - activeTim.setPk(rs.getInt("PK")); - activeTim.setItisCodes(new ArrayList()); - - Coordinate startPoint = null; - Coordinate endPoint = null; - - // Set startPoint - BigDecimal startLat = rs.getBigDecimal("START_LATITUDE"); - BigDecimal startLon = rs.getBigDecimal("START_LONGITUDE"); - if (!rs.wasNull()) { - startPoint = new Coordinate(startLat, startLon); - } - activeTim.setStartPoint(startPoint); - - // Set endPoint - BigDecimal endLat = rs.getBigDecimal("END_LATITUDE"); - BigDecimal endLon = rs.getBigDecimal("END_LONGITUDE"); - if (!rs.wasNull()) { - endPoint = new Coordinate(endLat, endLon); - } - activeTim.setEndPoint(endPoint); - - // Set timType - long timTypeId = rs.getLong("TIM_TYPE_ID"); - if (!rs.wasNull()) { - activeTim.setTimTypeId(timTypeId); - activeTim.setTimType(rs.getString("TYPE")); - } - - // Set projectKey - int projectKey = rs.getInt("PROJECT_KEY"); - if (!rs.wasNull()) { - activeTim.setProjectKey(projectKey); - } - } - - // Add the ITIS code to the ActiveTim's ITIS codes, if not null - var itisCode = rs.getInt("ITIS_CODE"); - if (!rs.wasNull()) { - activeTim.getItisCodes().add(itisCode); - } - } - - if (activeTim != null) { - results.add(activeTim); - } - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - return ResponseEntity.ok(results); - } - - /** - * Get all ActiveTims (including RSU address and RSU Index) - * - * @return List of ActiveTims, sorted by RSU and RSU Index - */ - @RequestMapping(value = "/active-rsu-tims", method = RequestMethod.GET) - public ResponseEntity> GetActiveRsuTims() { - List results = new ArrayList(); - ActiveTim activeTim = null; - - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - - try { - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - - String query = "select active_tim.*, rsu_view.ipv4_address, tim_rsu.rsu_index from active_tim"; - query += " inner join tim_rsu on active_tim.tim_id = tim_rsu.tim_id"; - query += " inner join rsu on tim_rsu.rsu_id = rsu.rsu_id"; - query += " inner join rsu_view on rsu.deviceid = rsu_view.deviceid"; - query += " where sat_record_id is null"; - query += " order by rsu_view.ipv4_address, tim_rsu.rsu_index"; // Required by ValidateRsus - - rs = statement.executeQuery(query); - - // convert to ActiveTim object - while (rs.next()) { - // Create ActiveTim record - activeTim = new ActiveTim(); - activeTim.setActiveTimId(rs.getLong("ACTIVE_TIM_ID")); - activeTim.setTimId(rs.getLong("TIM_ID")); - activeTim.setDirection(rs.getString("DIRECTION")); - activeTim.setStartDateTime(rs.getString("TIM_START")); - activeTim.setEndDateTime(rs.getString("TIM_END")); - activeTim.setExpirationDateTime(rs.getString("EXPIRATION_DATE")); - activeTim.setTimTypeId(rs.getLong("TIM_TYPE_ID")); - activeTim.setRoute(rs.getString("ROUTE")); - activeTim.setClientId(rs.getString("CLIENT_ID")); - activeTim.setSatRecordId(rs.getString("SAT_RECORD_ID")); - activeTim.setPk(rs.getInt("PK")); - activeTim.setRsuTarget(rs.getString("IPV4_ADDRESS")); - activeTim.setRsuIndex(rs.getInt("RSU_INDEX")); - Coordinate startPoint = null; - Coordinate endPoint = null; - BigDecimal startLat = rs.getBigDecimal("START_LATITUDE"); - BigDecimal startLon = rs.getBigDecimal("START_LONGITUDE"); - if (!rs.wasNull()) { - startPoint = new Coordinate(startLat, startLon); - } - activeTim.setStartPoint(startPoint); - - BigDecimal endLat = rs.getBigDecimal("END_LATITUDE"); - BigDecimal endLon = rs.getBigDecimal("END_LONGITUDE"); - if (!rs.wasNull()) { - endPoint = new Coordinate(endLat, endLon); - } - activeTim.setEndPoint(endPoint); - results.add(activeTim); - } - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - return ResponseEntity.ok(results); - } - - @RequestMapping(value = "/active-rsu-tim", method = RequestMethod.POST) - public ResponseEntity GetActiveRsuTim(@RequestBody ActiveRsuTimQueryModel artqm) { - - ActiveTim activeTim = null; - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - - try { - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - String query = "select * from active_tim"; - query += " inner join tim_rsu on active_tim.tim_id = tim_rsu.tim_id"; - query += " inner join rsu on tim_rsu.rsu_id = rsu.rsu_id"; - query += " inner join rsu_view on rsu.deviceid = rsu_view.deviceid"; - query += " where ipv4_address = '" + artqm.getIpv4() + "' and client_id = '" + artqm.getClientId() - + "' and active_tim.direction = '" + artqm.getDirection() + "'"; - - rs = statement.executeQuery(query); - List activeTims = getActiveTimFromRS(rs, false); - if (activeTims.size() > 0) { - activeTim = activeTims.get(activeTims.size() - 1); - } - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTim); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - return ResponseEntity.ok(activeTim); - } - - @RequestMapping(value = "/add", method = RequestMethod.POST) - public ResponseEntity InsertActiveTim(@RequestBody ActiveTim activeTim) { - - Connection connection = null; - PreparedStatement preparedStatement = null; - Long activeTimId = 0l; - try { - String insertQueryStatement = timDbTables.buildInsertQueryStatement("active_tim", - timDbTables.getActiveTimTable()); - - // get connection - connection = dbInteractions.getConnectionPool(); - - preparedStatement = connection.prepareStatement(insertQueryStatement, new String[] { "active_tim_id" }); - int fieldNum = 1; - - for (String col : timDbTables.getActiveTimTable()) { - if (col.equals("TIM_ID")) - sqlNullHandler.setLongOrNull(preparedStatement, fieldNum, activeTim.getTimId()); - else if (col.equals("DIRECTION")) - sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, activeTim.getDirection()); - else if (col.equals("TIM_START")) { - java.util.Date tim_start_date = utility.convertDate(activeTim.getStartDateTime()); - Timestamp tim_start_timestamp = new Timestamp(tim_start_date.getTime()); - sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, tim_start_timestamp); - } else if (col.equals("TIM_END")) { - if (activeTim.getEndDateTime() != null) { - java.util.Date tim_end_date = utility.convertDate(activeTim.getEndDateTime()); - Timestamp tim_end_timestamp = new Timestamp(tim_end_date.getTime()); - sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, tim_end_timestamp); - } else - preparedStatement.setNull(fieldNum, java.sql.Types.TIMESTAMP); - } else if (col.equals("EXPIRATION_DATE")) { - if (activeTim.getExpirationDateTime() != null) { - java.util.Date tim_exp_date = utility.convertDate(activeTim.getExpirationDateTime()); - Timestamp tim_exp_timestamp = new Timestamp(tim_exp_date.getTime()); - sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, tim_exp_timestamp); - } else { - preparedStatement.setNull(fieldNum, java.sql.Types.TIMESTAMP); - } - } else if (col.equals("TIM_TYPE_ID")) - sqlNullHandler.setLongOrNull(preparedStatement, fieldNum, activeTim.getTimTypeId()); - else if (col.equals("ROUTE")) - sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, activeTim.getRoute()); - else if (col.equals("CLIENT_ID")) - sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, activeTim.getClientId()); - else if (col.equals("SAT_RECORD_ID")) - sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, activeTim.getSatRecordId()); - else if (col.equals("PK")) - sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, activeTim.getPk()); - else if (col.equals("START_LATITUDE")) { - BigDecimal start_lat = null; - if (activeTim.getStartPoint() != null) - start_lat = activeTim.getStartPoint().getLatitude(); - sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, start_lat); - } else if (col.equals("START_LONGITUDE")) { - BigDecimal start_lon = null; - if (activeTim.getStartPoint() != null) - start_lon = activeTim.getStartPoint().getLongitude(); - sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, start_lon); - } else if (col.equals("END_LATITUDE")) { - BigDecimal end_lat = null; - if (activeTim.getEndPoint() != null) - end_lat = activeTim.getEndPoint().getLatitude(); - sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, end_lat); - } else if (col.equals("END_LONGITUDE")) { - BigDecimal end_lon = null; - if (activeTim.getEndPoint() != null) - end_lon = activeTim.getEndPoint().getLongitude(); - sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, end_lon); - } else if (col.equals("PROJECT_KEY")) { - sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, activeTim.getProjectKey()); - } - - fieldNum++; - } - - activeTimId = dbInteractions.executeAndLog(preparedStatement, "active tim"); - return ResponseEntity.ok(activeTimId); - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTimId); - } finally { - try { - // close prepared statement - if (preparedStatement != null) - preparedStatement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - } - - private List getActiveTimFromRS(ResultSet rs, boolean includeType) throws SQLException { - List activeTims = new ArrayList<>(); - ActiveTim activeTim = null; - - // convert to ActiveTim object - while (rs.next()) { - activeTim = new ActiveTim(); - activeTim.setActiveTimId(rs.getLong("ACTIVE_TIM_ID")); - activeTim.setTimId(rs.getLong("TIM_ID")); - activeTim.setSatRecordId(rs.getString("SAT_RECORD_ID")); - activeTim.setClientId(rs.getString("CLIENT_ID")); - activeTim.setDirection(rs.getString("DIRECTION")); - activeTim.setEndDateTime(rs.getString("TIM_END")); - activeTim.setStartDateTime(rs.getString("TIM_START")); - activeTim.setExpirationDateTime(rs.getString("EXPIRATION_DATE")); - activeTim.setRoute(rs.getString("ROUTE")); - activeTim.setPk(rs.getInt("PK")); - activeTim.setTimTypeId(rs.getLong("TIM_TYPE_ID")); - - if (includeType) { - activeTim.setTimType(rs.getString("TYPE")); - } - - Coordinate startPoint = null; - Coordinate endPoint = null; - BigDecimal startLat = rs.getBigDecimal("START_LATITUDE"); - BigDecimal startLon = rs.getBigDecimal("START_LONGITUDE"); - if (!rs.wasNull()) { - startPoint = new Coordinate(startLat, startLon); - } - activeTim.setStartPoint(startPoint); - - BigDecimal endLat = rs.getBigDecimal("END_LATITUDE"); - BigDecimal endLon = rs.getBigDecimal("END_LONGITUDE"); - if (!rs.wasNull()) { - endPoint = new Coordinate(endLat, endLon); - } - activeTim.setEndPoint(endPoint); - - activeTims.add(activeTim); - } - - return activeTims; - } - - private String translateIso8601ToTimestampFormat(String date) throws ParseException { - DateFormat sdf = new SimpleDateFormat("dd-MMM-yy hh.mm.ss.SSS a"); - // TimeZone toTimeZone = TimeZone.getTimeZone("MST"); - // sdf.setTimeZone(toTimeZone); - DateFormat m_ISO8601Local = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - Date dte = m_ISO8601Local.parse(date); - return sdf.format(dte.getTime()); - } - - private List> SplitMaxList(List activeTimIds, int maxListSize) { - Long[] elements = activeTimIds.toArray(new Long[0]); - int maxChunks = (int) Math.ceil(elements.length / (double) maxListSize); - List> returnValues = new ArrayList>(maxChunks); - - for (int i = 0; i < maxChunks; i++) { - int from = i * maxListSize; - int to = Math.min(from + maxListSize, elements.length); - Long[] range = Arrays.copyOfRange(elements, from, to); - returnValues.add(Arrays.asList(range)); - } - return returnValues; - } - - @RequestMapping(value = "/reset-expiration-date", method = RequestMethod.PUT, headers = "Accept=application/json") - public ResponseEntity ResetExpirationDate(@RequestBody List activeTimIds) { - if (activeTimIds == null || activeTimIds.size() == 0) { - return ResponseEntity.ok(true); - } - - boolean result = true; - // on occasion we have over 1000 active tim ids, resulting in error - // split this out by 500 records at a time to avoid issues - List> splitActiveTims = SplitMaxList(activeTimIds, 500); - Connection connection = null; - PreparedStatement preparedStatement = null; - - try { - connection = dbInteractions.getConnectionPool(); - - for (int splitTimsIndex = 0; splitTimsIndex < splitActiveTims.size(); splitTimsIndex++) { - List splitTims = splitActiveTims.get(splitTimsIndex); - String updateSql = "UPDATE ACTIVE_TIM SET EXPIRATION_DATE = NULL WHERE ACTIVE_TIM_ID IN ("; - - for (int i = 0; i < splitTims.size(); i++) { - updateSql += "?,"; - } - updateSql = updateSql.substring(0, updateSql.length() - 1); - updateSql += ")"; - - preparedStatement = connection.prepareStatement(updateSql); - for (int i = 0; i < splitTims.size(); i++) { - preparedStatement.setLong(i + 1, splitTims.get(i)); - } - - // execute delete SQL statement - result &= dbInteractions.updateOrDelete(preparedStatement); - } - - System.out.println("Reset expiration date for Active Tims (active_tim_ids " - + activeTimIds.stream().map(String::valueOf).collect(Collectors.joining(",")) + ")"); - - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(false); - } finally { - try { - // close prepared statement - if (preparedStatement != null) - preparedStatement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - return ResponseEntity.ok(result); - } - - @RequestMapping(value = "/update-expiration/{packetID}/{expDate}", method = RequestMethod.PUT) - public ResponseEntity UpdateExpiration(@PathVariable String packetID, @PathVariable String expDate) { - Connection connection = null; - PreparedStatement preparedStatement = null; - boolean success = false; - - String query = "SELECT ACTIVE_TIM_ID FROM ACTIVE_TIM atim"; - query += " INNER JOIN TIM ON atim.TIM_ID = TIM.TIM_ID"; - query += " WHERE TIM.PACKET_ID = ?"; - - String updateStatement = "UPDATE ACTIVE_TIM SET EXPIRATION_DATE = ? WHERE ACTIVE_TIM_ID IN ("; - updateStatement += query; - updateStatement += ")"; - - try { - connection = dbInteractions.getConnectionPool(); - preparedStatement = connection.prepareStatement(updateStatement); - Date date = utility.convertDate(expDate); - Timestamp expDateTimestamp = new Timestamp(date.getTime()); - preparedStatement.setTimestamp(1, expDateTimestamp);// expDate comes in as MST from previously called function - // (GetMinExpiration) - preparedStatement.setObject(2, packetID); - - // execute update statement - success = dbInteractions.updateOrDelete(preparedStatement); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(false); - } finally { - try { - // close prepared statement - if (preparedStatement != null) - preparedStatement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - utility.logWithDate(String.format("Called UpdateExpiration with packetID: %s, expDate: %s. Successful: %s", - packetID, expDate, success)); - return ResponseEntity.ok(success); - } - - @RequestMapping(value = "/get-min-expiration/{packetID}/{expDate}") - public ResponseEntity GetMinExpiration(@PathVariable String packetID, @PathVariable String expDate) - throws ParseException { - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - String minStart = ""; - - try { - // Fetch the minimum of passed in expDate and database held - // active_tim.expiration_date. To compare like values we convert the expDate - // TO_TIMESTAMP. Without this it compares string length. - // Also, there are some null values in the db. To get around these, we use the - // coalesce function with the expDate passed in value. - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - String targetFormat = "DD-MON-YYYY HH12.MI.SS a"; - String selectTimestamp = String.format("SELECT TO_TIMESTAMP('%s', '%s')", - translateIso8601ToTimestampFormat(expDate), targetFormat); - - - String minExpDate = "SELECT MIN(EXPIRATION_DATE) FROM ACTIVE_TIM atim"; - minExpDate += " INNER JOIN TIM ON atim.TIM_ID = TIM.TIM_ID"; - minExpDate += " WHERE TIM.PACKET_ID = '" + packetID + "'"; - - String query = String.format("SELECT LEAST((%s), (COALESCE((%s),(%s)))) minStart", - selectTimestamp, minExpDate, selectTimestamp); - rs = statement.executeQuery(query); - while (rs.next()) { - var tmpTs = rs.getTimestamp("MINSTART", UTCCalendar); - minStart = utility.timestampFormat.format(tmpTs); - } - } catch (SQLException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(minStart); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - utility.logWithDate(String.format("Called GetMinExpiration with packetID: %s, expDate: %s. Min start date: %s", - packetID, expDate, minStart)); - return ResponseEntity.ok(minStart); - } - - @RequestMapping(value = "/mark-for-deletion/{activeTimId}", method = RequestMethod.PUT) - public ResponseEntity MarkForDeletion(@PathVariable Long activeTimId) { - Connection connection = null; - PreparedStatement preparedStatement = null; - boolean success = false; - - String updateStatement = "UPDATE ACTIVE_TIM SET MARKED_FOR_DELETION = '1' WHERE ACTIVE_TIM_ID = ?"; - - try { - connection = dbInteractions.getConnectionPool(); - preparedStatement = connection.prepareStatement(updateStatement); - preparedStatement.setLong(1, activeTimId); - - // execute update statement - success = dbInteractions.updateOrDelete(preparedStatement); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(false); - } finally { - try { - // close prepared statement - if (preparedStatement != null) - preparedStatement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - if (!success) { - utility.logWithDate(String.format("Failed to mark active tim for deletion with activeTimId: %s", activeTimId)); + BigDecimal endLat = rs.getBigDecimal("END_LATITUDE"); + BigDecimal endLon = rs.getBigDecimal("END_LONGITUDE"); + if (!rs.wasNull()) { + endPoint = new Coordinate(endLat, endLon); } - return ResponseEntity.ok(success); + timUpdateModel.setEndPoint(endPoint); + + timUpdateModel.setStartDate_Timestamp(rs.getTimestamp("TIM_START", UTCCalendar)); + timUpdateModel.setEndDate_Timestamp(rs.getTimestamp("TIM_END", UTCCalendar)); + + // Tim properties + timUpdateModel.setMsgCnt(rs.getInt("MSG_CNT")); + timUpdateModel.setUrlB(rs.getString("URL_B")); + timUpdateModel.setPacketId(rs.getString("PACKET_ID")); + + // Tim Type properties + timUpdateModel.setTimTypeName(rs.getString("TIM_TYPE_NAME")); + timUpdateModel.setTimTypeDescription(rs.getString("TIM_TYPE_DESCRIPTION")); + + // Region Properties + timUpdateModel.setRegionId(rs.getInt("REGION_ID")); + timUpdateModel.setAnchorLat(rs.getBigDecimal("ANCHOR_LAT")); + timUpdateModel.setAnchorLong(rs.getBigDecimal("ANCHOR_LONG")); + + timUpdateModel.setLaneWidth(rs.getBigDecimal("LANE_WIDTH")); + timUpdateModel.setRegionDirection(rs.getString("REGION_DIRECTION")); + timUpdateModel.setDirectionality(rs.getString("DIRECTIONALITY")); + timUpdateModel.setClosedPath(rs.getBoolean("CLOSED_PATH")); + timUpdateModel.setPathId(rs.getInt("PATH_ID")); + timUpdateModel.setRegionDescription(rs.getString("REGION_DESCRIPTION")); + + // DataFrame properties + timUpdateModel.setDataFrameId(rs.getInt("DATA_FRAME_ID")); + timUpdateModel.setDurationTime(rs.getInt("DURATION_TIME")); + timUpdateModel.setUrl(rs.getString("URL")); + return timUpdateModel; } } diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ActiveTimHoldingController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ActiveTimHoldingController.java index a4e7c16da..00a92b26c 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ActiveTimHoldingController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/ActiveTimHoldingController.java @@ -1,5 +1,6 @@ package com.trihydro.cvdatacontroller.controller; +import com.trihydro.library.model.ActiveTimHoldingDeleteModel; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -14,6 +15,8 @@ import com.trihydro.library.model.Coordinate; import com.trihydro.library.tables.TimDbTables; +import java.util.Objects; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -30,6 +33,7 @@ @RestController @RequestMapping("active-tim-holding") @ApiIgnore +@Slf4j public class ActiveTimHoldingController extends BaseController { private TimDbTables timDbTables; private SQLNullHandler sqlNullHandler; @@ -40,21 +44,15 @@ public void InjectDependencies(TimDbTables _timDbTables, SQLNullHandler _sqlNull sqlNullHandler = _sqlNullHandler; } - @RequestMapping(value = "/add", method = RequestMethod.POST) + @RequestMapping(value = "/add", method = RequestMethod.POST, produces = "application/json") public ResponseEntity InsertActiveTimHolding(@RequestBody ActiveTimHolding activeTimHolding) { - Connection connection = null; - PreparedStatement preparedStatement = null; - Long activeTimHoldingId = 0l; - try { - String insertQueryStatement = timDbTables.buildInsertQueryStatement("active_tim_holding", - timDbTables.getActiveTimHoldingTable()); + Long activeTimHoldingId = 0L; - // get connection - connection = dbInteractions.getConnectionPool(); + String insertQueryStatement = timDbTables.buildInsertQueryStatement("active_tim_holding", timDbTables.getActiveTimHoldingTable()); - preparedStatement = connection.prepareStatement(insertQueryStatement, - new String[] { "active_tim_holding_id" }); + try (Connection connection = dbInteractions.getConnectionPool(); + PreparedStatement preparedStatement = connection.prepareStatement(insertQueryStatement, new String[] {"active_tim_holding_id"})) { int fieldNum = 1; for (String col : timDbTables.getActiveTimHoldingTable()) { @@ -70,23 +68,19 @@ public ResponseEntity InsertActiveTimHolding(@RequestBody ActiveTimHolding sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, activeTimHolding.getSatRecordId()); } else if (col.equals("START_LATITUDE")) { if (activeTimHolding.getStartPoint() != null) { - sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, - activeTimHolding.getStartPoint().getLatitude()); + sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, activeTimHolding.getStartPoint().getLatitude()); } } else if (col.equals("START_LONGITUDE")) { if (activeTimHolding.getStartPoint() != null) { - sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, - activeTimHolding.getStartPoint().getLongitude()); + sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, activeTimHolding.getStartPoint().getLongitude()); } } else if (col.equals("END_LATITUDE")) { if (activeTimHolding.getEndPoint() != null) { - sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, - activeTimHolding.getEndPoint().getLatitude()); + sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, activeTimHolding.getEndPoint().getLatitude()); } } else if (col.equals("END_LONGITUDE")) { if (activeTimHolding.getEndPoint() != null) { - sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, - activeTimHolding.getEndPoint().getLongitude()); + sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, activeTimHolding.getEndPoint().getLongitude()); } } else if (col.equals("RSU_INDEX")) { sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, activeTimHolding.getRsuIndex()); @@ -99,8 +93,7 @@ public ResponseEntity InsertActiveTimHolding(@RequestBody ActiveTimHolding } else if (col.equals("EXPIRATION_DATE")) { if (activeTimHolding.getExpirationDateTime() != null) { java.util.Date tim_exp_date = utility.convertDate(activeTimHolding.getExpirationDateTime()); - sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, - utility.timestampFormat.format(tim_exp_date)); + sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, utility.timestampFormat.format(tim_exp_date)); } else { preparedStatement.setNull(fieldNum, java.sql.Types.TIMESTAMP); } @@ -115,74 +108,46 @@ public ResponseEntity InsertActiveTimHolding(@RequestBody ActiveTimHolding if (activeTimHoldingId == null) { // this already exists, fetch it and return the id - Statement statement = null; - ResultSet rs = null; - try { - statement = connection.createStatement(); - String query = "select active_tim_holding_id from active_tim_holding"; - if (activeTimHolding.getSatRecordId() != null && activeTimHolding.getSatRecordId() != "") { - // sat tim - query += " where sat_record_id = '" + activeTimHolding.getSatRecordId() + "' and client_id = '" - + activeTimHolding.getClientId() + "' and direction = '" - + activeTimHolding.getDirection() + "'"; - } else { - // rsu tim - query += " where rsu_target = '" + activeTimHolding.getRsuTarget() + "' and client_id = '" - + activeTimHolding.getClientId() + "' and direction = '" - + activeTimHolding.getDirection() + "'"; - } - rs = statement.executeQuery(query); + String query = "select active_tim_holding_id from active_tim_holding"; + if (activeTimHolding.getSatRecordId() != null && !Objects.equals(activeTimHolding.getSatRecordId(), "")) { + // sat tim + query += " where sat_record_id = '" + activeTimHolding.getSatRecordId() + "' and client_id = '" + activeTimHolding.getClientId() + "' and direction = '" + + activeTimHolding.getDirection() + "'"; + } else { + // rsu tim + query += + " where rsu_target = '" + activeTimHolding.getRsuTarget() + "' and client_id = '" + activeTimHolding.getClientId() + "' and direction = '" + activeTimHolding.getDirection() + + "'"; + } + + try (Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(query)) { + while (rs.next()) { activeTimHoldingId = rs.getLong("ACTIVE_TIM_HOLDING_ID"); } } catch (Exception e) { - e.printStackTrace(); + log.error("Error getting active tim holding id", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTimHoldingId); - } finally { - try { - if (statement != null) - statement.close(); - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } } } return ResponseEntity.ok(activeTimHoldingId); } catch (SQLException e) { - e.printStackTrace(); + log.error("Error inserting active tim holding", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(activeTimHoldingId); - } finally { - try { - // close prepared statement - if (preparedStatement != null) - preparedStatement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } } } - @RequestMapping(value = "/get-rsu/{ipv4Address}", method = RequestMethod.GET) + @RequestMapping(value = "/get-rsu/{ipv4Address}", method = RequestMethod.GET, produces = "application/json") public ResponseEntity> getActiveTimHoldingForRsu(@PathVariable String ipv4Address) { - ActiveTimHolding activeTimHolding = null; - Connection connection = null; - Statement statement = null; - ResultSet rs = null; + ActiveTimHolding activeTimHolding; + List holdings = new ArrayList<>(); - try { - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - String query = "select * from active_tim_holding "; - query += " where rsu_target = '" + ipv4Address + "'"; - rs = statement.executeQuery(query); + String query = "select * from active_tim_holding "; + query += " where rsu_target = '" + ipv4Address + "'"; + try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(query)) { // convert to ActiveTim object while (rs.next()) { activeTimHolding = new ActiveTimHolding(); @@ -191,10 +156,8 @@ public ResponseEntity> getActiveTimHoldingForRsu(@PathVar activeTimHolding.setDirection(rs.getString("DIRECTION")); activeTimHolding.setRsuTargetId(rs.getString("RSU_TARGET")); activeTimHolding.setSatRecordId(rs.getString("SAT_RECORD_ID")); - activeTimHolding.setStartPoint( - new Coordinate(rs.getBigDecimal("START_LATITUDE"), rs.getBigDecimal("START_LONGITUDE"))); - activeTimHolding.setEndPoint( - new Coordinate(rs.getBigDecimal("END_LATITUDE"), rs.getBigDecimal("END_LONGITUDE"))); + activeTimHolding.setStartPoint(new Coordinate(rs.getBigDecimal("START_LATITUDE"), rs.getBigDecimal("START_LONGITUDE"))); + activeTimHolding.setEndPoint(new Coordinate(rs.getBigDecimal("END_LATITUDE"), rs.getBigDecimal("END_LONGITUDE"))); activeTimHolding.setDateCreated(rs.getString("DATE_CREATED")); int rsu_index = rs.getInt("RSU_INDEX"); if (!rs.wasNull()) { @@ -206,22 +169,55 @@ public ResponseEntity> getActiveTimHoldingForRsu(@PathVar } return ResponseEntity.ok(holdings); } catch (SQLException e) { - e.printStackTrace(); + log.error("Error getting active tim holding for rsu", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(holdings); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); + } + } + + @RequestMapping(value = "/get-all", produces = "application/json", method = RequestMethod.GET) + public ResponseEntity> getAllRecords() { + List holdings = new ArrayList<>(); + + String query = "select * from active_tim_holding"; + + try (Connection connection = dbInteractions.getConnectionPool(); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(query)) { + // convert to ActiveTim object + while (rs.next()) { + ActiveTimHolding activeTimHolding = new ActiveTimHolding(); + activeTimHolding.setActiveTimHoldingId(rs.getLong("ACTIVE_TIM_HOLDING_ID")); + activeTimHolding.setClientId(rs.getString("CLIENT_ID")); + activeTimHolding.setDirection(rs.getString("DIRECTION")); + activeTimHolding.setRsuTargetId(rs.getString("RSU_TARGET")); + activeTimHolding.setSatRecordId(rs.getString("SAT_RECORD_ID")); + activeTimHolding.setStartPoint(new Coordinate(rs.getBigDecimal("START_LATITUDE"), rs.getBigDecimal("START_LONGITUDE"))); + activeTimHolding.setEndPoint(new Coordinate(rs.getBigDecimal("END_LATITUDE"), rs.getBigDecimal("END_LONGITUDE"))); + activeTimHolding.setDateCreated(rs.getString("DATE_CREATED")); + int rsu_index = rs.getInt("RSU_INDEX"); + if (!rs.wasNull()) { + activeTimHolding.setRsuIndex(rsu_index); + } + activeTimHolding.setExpirationDateTime(rs.getString("EXPIRATION_DATE")); + activeTimHolding.setPacketId(rs.getString("PACKET_ID")); + holdings.add(activeTimHolding); } + return ResponseEntity.ok(holdings); + } catch (SQLException e) { + log.error("Error getting all active tim holdings", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(holdings); + } + } + + @RequestMapping(value = "/delete", produces = "application/json", method = RequestMethod.DELETE) + public ResponseEntity deleteActiveTimHoldingRecords(@RequestBody ActiveTimHoldingDeleteModel activeTimHoldingDeleteModel) { + String deleteQueryStatement = "delete from active_tim_holding where active_tim_holding_id = ANY (?)"; + + try (Connection connection = dbInteractions.getConnectionPool(); PreparedStatement preparedStatement = connection.prepareStatement(deleteQueryStatement)) { + preparedStatement.setArray(1, connection.createArrayOf("long", activeTimHoldingDeleteModel.getIds().toArray())); + dbInteractions.executeAndLog(preparedStatement, "active tim holding"); + return ResponseEntity.ok(true); + } catch (SQLException e) { + log.error("Error deleting active tim holding records", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(false); } } } \ No newline at end of file diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/DataFrameController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/DataFrameController.java index 8a63fb457..2c8c3950b 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/DataFrameController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/DataFrameController.java @@ -14,9 +14,6 @@ import java.util.List; import java.util.TimeZone; -import com.trihydro.library.helpers.SQLNullHandler; -import com.trihydro.library.tables.TimDbTables; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -27,6 +24,9 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; +import com.trihydro.library.helpers.SQLNullHandler; +import com.trihydro.library.tables.TimDbTables; + import springfox.documentation.annotations.ApiIgnore; import us.dot.its.jpo.ode.plugin.j2735.OdeTravelerInformationMessage.DataFrame; @@ -45,7 +45,7 @@ public void InjectDependencies(TimDbTables _timDbTables, SQLNullHandler _sqlNull sqlNullHandler = _sqlNullHandler; } - @RequestMapping(method = RequestMethod.GET, value = "/itis-for-data-frame/{dataFrameId}") + @RequestMapping(method = RequestMethod.GET, produces = "application/json", value = "/itis-for-data-frame/{dataFrameId}") public ResponseEntity GetItisCodesForDataFrameId(@PathVariable Integer dataFrameId) { Connection connection = null; Statement statement = null; @@ -111,8 +111,6 @@ public ResponseEntity AddDataFrame(@RequestBody DataFrame dFrame, @PathVar for (String col : timDbTables.getDataFrameTable()) { if (col.equals("TIM_ID")) { sqlNullHandler.setLongOrNull(preparedStatement, fieldNum, timId); - } else if (col.equals("SSP_TIM_RIGHTS")) { - sqlNullHandler.setShortOrNull(preparedStatement, fieldNum, dFrame.getNotUsed()); } else if (col.equals("FRAME_TYPE")) { Integer ordinal = null; if (dFrame.getFrameType() != null) { @@ -123,12 +121,6 @@ public ResponseEntity AddDataFrame(@RequestBody DataFrame dFrame, @PathVar sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, dFrame.getDurationTime()); } else if (col.equals("PRIORITY")) { sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, dFrame.getPriority()); - } else if (col.equals("SSP_LOCATION_RIGHTS")) { - sqlNullHandler.setShortOrNull(preparedStatement, fieldNum, dFrame.getNotUsed1()); - } else if (col.equals("SSP_MSG_TYPES")) { - sqlNullHandler.setShortOrNull(preparedStatement, fieldNum, dFrame.getNotUsed3()); - } else if (col.equals("SSP_MSG_CONTENT")) { - sqlNullHandler.setShortOrNull(preparedStatement, fieldNum, dFrame.getNotUsed2()); } else if (col.equals("CONTENT")) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, dFrame.getContent()); } else if (col.equals("URL")) { diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/MilepostController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/MilepostController.java index 789f05b3f..5207aa539 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/MilepostController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/MilepostController.java @@ -281,7 +281,7 @@ private String translateDirection(String direction) { * @param wydotTim * @return Collection of Milepost objects representing the path */ - @RequestMapping(method = RequestMethod.POST, value = "/get-milepost-start-end") + @RequestMapping(method = RequestMethod.POST, produces = "application/json", value = "/get-milepost-start-end") public ResponseEntity> getMilepostsByStartEndPoint( @RequestBody WydotTim wydotTim) { @@ -350,7 +350,7 @@ public ResponseEntity getMilepostsFeatureCollectionByStartEndPoint(@Requ return ResponseEntity.ok(fc.toJson()); } - @RequestMapping(method = RequestMethod.POST, value = "/get-milepost-single-point") + @RequestMapping(method = RequestMethod.POST, produces = "application/json", value = "/get-milepost-single-point") public ResponseEntity> getMilepostsByPointWithBuffer( @RequestBody MilepostBuffer milepostBuffer) { // check startPoint diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/NodeXYController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/NodeXYController.java index c65e98ced..9975e45fd 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/NodeXYController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/NodeXYController.java @@ -56,19 +56,19 @@ else if (col.equals("NODE_LAT")) else if (col.equals("NODE_LONG")) sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, nodeXY.getNodeLong()); else if (col.equals("X")) - sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, nodeXY.getX()); + sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, nodeXY.getXpos()); else if (col.equals("Y")) - sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, nodeXY.getY()); + sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, nodeXY.getYpos()); else if (col.equals("ATTRIBUTES_DWIDTH")) if (nodeXY.getAttributes() != null) sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, - nodeXY.getAttributes().getdWidth()); + nodeXY.getAttributes().getDwidth()); else preparedStatement.setString(fieldNum, null); else if (col.equals("ATTRIBUTES_DELEVATION")) if (nodeXY.getAttributes() != null) sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, - nodeXY.getAttributes().getdElevation()); + nodeXY.getAttributes().getDelevation()); else preparedStatement.setString(fieldNum, null); fieldNum++; diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/PathNodeXYController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/PathNodeXYController.java index a98f6b2bd..3fa1e6a04 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/PathNodeXYController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/PathNodeXYController.java @@ -62,8 +62,8 @@ public ResponseEntity GetNodeXYForPath(@PathVariable int pathId) { nodexy.setDelta(rs.getString("DELTA")); nodexy.setNodeLat(rs.getBigDecimal("NODE_LAT")); nodexy.setNodeLong(rs.getBigDecimal("NODE_LONG")); - nodexy.setX(rs.getBigDecimal("X")); - nodexy.setY(rs.getBigDecimal("Y")); + nodexy.setXpos(rs.getBigDecimal("X")); + nodexy.setYpos(rs.getBigDecimal("Y")); nodeXYs.add(nodexy); } diff --git a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/RsuController.java b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/RsuController.java index a3ded3655..c4a9fe3b5 100644 --- a/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/RsuController.java +++ b/cv-data-controller/src/main/java/com/trihydro/cvdatacontroller/controller/RsuController.java @@ -118,7 +118,7 @@ public ResponseEntity> SelectActiveRsus() { return ResponseEntity.ok(rsus); } - @RequestMapping(method = RequestMethod.GET, value = "/rsus-for-tim/{timId}") + @RequestMapping(method = RequestMethod.GET, produces = "application/json", value = "/rsus-for-tim/{timId}") public ResponseEntity> GetFullRsusTimIsOn(@PathVariable Long timId) { List rsus = new ArrayList(); Connection connection = null; @@ -168,7 +168,7 @@ public ResponseEntity> GetFullRsusTimIsOn(@PathVariable Long t return ResponseEntity.ok(rsus); } - @RequestMapping(method = RequestMethod.GET, value = "/rsus-by-route/{route}") + @RequestMapping(method = RequestMethod.GET, produces = "application/json", value = "/rsus-by-route/{route}") public ResponseEntity> SelectRsusByRoute(@PathVariable String route) { ArrayList rsus = new ArrayList(); Connection connection = null; @@ -215,7 +215,7 @@ public ResponseEntity> SelectRsusByRoute(@PathVariable Strin return ResponseEntity.ok(rsus); } - @RequestMapping(method = RequestMethod.GET, value = "/active-rsu-tim-indexes/{rsuId}") + @RequestMapping(method = RequestMethod.GET, produces = "application/json", value = "/active-rsu-tim-indexes/{rsuId}") public ResponseEntity> GetActiveRsuTimIndexes(@PathVariable Integer rsuId) { List indexes = new ArrayList(); diff --git a/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/ActiveTimControllerTest.java b/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/ActiveTimControllerTest.java index 304076420..b52ca03fb 100644 --- a/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/ActiveTimControllerTest.java +++ b/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/ActiveTimControllerTest.java @@ -4,6 +4,7 @@ import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -34,6 +35,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.client.HttpServerErrorException; import us.dot.its.jpo.ode.plugin.j2735.timstorage.FrameType; public class ActiveTimControllerTest extends TestBase { @@ -48,8 +50,7 @@ public void setupSubTest() { } private void setupPreparedStatement() { - doReturn(mockPreparedStatement).when(mockTimDbTables).buildUpdateStatement(any(), any(), any(), any(), - any()); + doReturn(mockPreparedStatement).when(mockTimDbTables).buildUpdateStatement(any(), any(), any(), any(), any()); } @Test @@ -337,15 +338,17 @@ public void GetActiveTimsNotSent_FAIL() throws SQLException { @Test public void GetExpiredActiveTims_SUCCESS() throws SQLException { // Arrange - String statementStr = "select * from ACTIVE_TIM"; - statementStr += " WHERE TIM_END <= (NOW() AT TIME ZONE 'UTC')"; + String statementStr = "SELECT * FROM ACTIVE_TIM"; + statementStr += " WHERE TIM_END <= (NOW() AT TIME ZONE 'UTC') LIMIT ?"; // Act - ResponseEntity> aTims = uut.GetExpiredActiveTims(); + ResponseEntity> aTims = uut.GetExpiredActiveTims(500); // Assert Assertions.assertEquals(HttpStatus.OK, aTims.getStatusCode()); - verify(mockStatement).executeQuery(statementStr); + verify(mockConnection).prepareStatement(statementStr); + verify(mockPreparedStatement).setInt(1, 500); + verify(mockPreparedStatement).executeQuery(); verify(mockRs).getLong("ACTIVE_TIM_ID"); verify(mockRs).getLong("TIM_ID"); verify(mockRs).getString("SAT_RECORD_ID"); @@ -356,7 +359,7 @@ public void GetExpiredActiveTims_SUCCESS() throws SQLException { verify(mockRs).getString("CLIENT_ID"); verify(mockRs).getString("ROUTE"); verify(mockRs).getString("DIRECTION"); - verify(mockStatement).close(); + verify(mockPreparedStatement).close(); verify(mockConnection).close(); Assertions.assertEquals(1, aTims.getBody().size()); } @@ -364,19 +367,18 @@ public void GetExpiredActiveTims_SUCCESS() throws SQLException { @Test public void GetExpiredActiveTims_FAIL() throws SQLException { // Arrange - String statementStr = "select * from ACTIVE_TIM"; - statementStr += " WHERE TIM_END <= (NOW() AT TIME ZONE 'UTC')"; - when(mockStatement.executeQuery(isA(String.class))).thenThrow(new SQLException()); + String statementStr = "SELECT * FROM ACTIVE_TIM"; + statementStr += " WHERE TIM_END <= (NOW() AT TIME ZONE 'UTC') LIMIT ?"; + when(mockPreparedStatement.executeQuery()).thenThrow(new SQLException()); // Act - ResponseEntity> aTims = uut.GetExpiredActiveTims(); + Assertions.assertThrows(HttpServerErrorException.class, () -> uut.GetExpiredActiveTims(500)); // Assert - Assertions.assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, aTims.getStatusCode()); - verify(mockStatement).executeQuery(statementStr); - verify(mockStatement).close(); + verify(mockConnection).prepareStatement(statementStr); + verify(mockPreparedStatement).executeQuery(); + verify(mockPreparedStatement).close(); verify(mockConnection).close(); - Assertions.assertEquals(0, aTims.getBody().size()); } @Test @@ -431,8 +433,7 @@ public void GetActiveTimsByClientIdDirection_SUCCESS() throws SQLException { String clientId = "clientId"; Long timTypeId = -1l; String direction = "eastward"; - String selectStatement = "select * from active_tim where CLIENT_ID like '" + clientId + "' and TIM_TYPE_ID = " - + timTypeId; + String selectStatement = "select * from active_tim where CLIENT_ID like '" + clientId + "' and TIM_TYPE_ID = " + timTypeId; selectStatement += " and DIRECTION = '" + direction + "'"; selectStatement += " and MARKED_FOR_DELETION = '0'"; @@ -466,8 +467,7 @@ public void GetActiveTimsByClientIdDirection_FAIL() throws SQLException { String clientId = "clientId"; Long timTypeId = -1l; String direction = "eastward"; - String selectStatement = "select * from active_tim where CLIENT_ID like '" + clientId + "' and TIM_TYPE_ID = " - + timTypeId; + String selectStatement = "select * from active_tim where CLIENT_ID like '" + clientId + "' and TIM_TYPE_ID = " + timTypeId; selectStatement += " and DIRECTION = '" + direction + "'"; selectStatement += " and MARKED_FOR_DELETION = '0'"; doThrow(new SQLException()).when(mockRs).getLong("ACTIVE_TIM_ID"); @@ -487,8 +487,7 @@ public void GetActiveTimsByClientIdDirection_FAIL() throws SQLException { public void GetBufferTimsByClientId_SUCCESS() throws SQLException { // Arrange String clientId = "clientId"; - String selectStatement = "select * from active_tim where CLIENT_ID like '" + clientId - + "\\%BUFF_-%' ESCAPE '\\'"; + String selectStatement = "select * from active_tim where CLIENT_ID like '" + clientId + "\\%BUFF_-%' ESCAPE '\\'"; // Act ResponseEntity> data = uut.GetBufferTimsByClientId(clientId); @@ -519,8 +518,7 @@ public void GetBufferTimsByClientId_FAIL() throws SQLException { // Arrange String clientId = "clientId"; - String selectStatement = "select * from active_tim where CLIENT_ID like '" + clientId - + "\\%BUFF_-%' ESCAPE '\\'"; + String selectStatement = "select * from active_tim where CLIENT_ID like '" + clientId + "\\%BUFF_-%' ESCAPE '\\'"; doThrow(new SQLException()).when(mockRs).getLong("ACTIVE_TIM_ID"); @@ -563,8 +561,9 @@ public void GetItisCodesForActiveTim_SUCCESS() throws SQLException { public void GetItisCodesForActiveTim_FAIL() throws SQLException { // Arrange Long activeTimId = -1l; - String selectStatement = "select itis_code from active_tim inner join tim on tim.tim_id = active_tim.tim_id inner join data_frame on tim.tim_id = data_frame.tim_id inner join data_frame_itis_code on data_frame_itis_code.data_frame_id = data_frame.data_frame_id inner join itis_code on data_frame_itis_code.itis_code_id = itis_code.itis_code_id where active_tim_id = " - + activeTimId + " order by data_frame_itis_code.position asc"; + String selectStatement = + "select itis_code from active_tim inner join tim on tim.tim_id = active_tim.tim_id inner join data_frame on tim.tim_id = data_frame.tim_id inner join data_frame_itis_code on data_frame_itis_code.data_frame_id = data_frame.data_frame_id inner join itis_code on data_frame_itis_code.itis_code_id = itis_code.itis_code_id where active_tim_id = " + + activeTimId + " order by data_frame_itis_code.position asc"; doThrow(new SQLException()).when(mockRs).getInt("ITIS_CODE"); // Act @@ -1148,14 +1147,10 @@ public void InsertActiveTim_SUCCESS() throws SQLException { verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 7, activeTim.getClientId());// CLIENT_ID verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 8, activeTim.getSatRecordId());// SAT_RECORD_ID verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 9, activeTim.getPk());// PK - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 10, - activeTim.getStartPoint().getLatitude());// START_LATITUDE - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 11, - activeTim.getStartPoint().getLongitude());// START_LONGITUDE - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 12, - activeTim.getEndPoint().getLatitude());// END_LATITUDE - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 13, - activeTim.getEndPoint().getLongitude());// END_LONGITUDE + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 10, activeTim.getStartPoint().getLatitude());// START_LATITUDE + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 11, activeTim.getStartPoint().getLongitude());// START_LONGITUDE + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 12, activeTim.getEndPoint().getLatitude());// END_LATITUDE + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 13, activeTim.getEndPoint().getLongitude());// END_LONGITUDE verify(mockPreparedStatement).close(); verify(mockConnection).close(); @@ -1169,8 +1164,7 @@ public void InsertActiveTim_FAIL() throws SQLException { String endTime = Instant.now().plusSeconds(60).toString(); activeTim.setStartDateTime(startTime); activeTim.setEndDateTime(endTime); - doThrow(new SQLException()).when(mockSqlNullHandler).setLongOrNull(mockPreparedStatement, 1, - activeTim.getTimId()); + doThrow(new SQLException()).when(mockSqlNullHandler).setLongOrNull(mockPreparedStatement, 1, activeTim.getTimId()); // Act ResponseEntity data = uut.InsertActiveTim(activeTim); @@ -1194,8 +1188,7 @@ public void ResetExpirationDate_SUCCESS() throws SQLException { // Assert Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); Assertions.assertTrue(data.getBody(), "Fail return on success"); - verify(mockConnection) - .prepareStatement("UPDATE ACTIVE_TIM SET EXPIRATION_DATE = NULL WHERE ACTIVE_TIM_ID IN (?)"); + verify(mockConnection).prepareStatement("UPDATE ACTIVE_TIM SET EXPIRATION_DATE = NULL WHERE ACTIVE_TIM_ID IN (?)"); verify(mockPreparedStatement).setLong(1, -1l); verify(mockPreparedStatement).close(); verify(mockConnection).close(); @@ -1228,18 +1221,16 @@ public void ResetExpirationDate_SUCCESS_Over_500_Records() throws SQLException { } updateTwo = updateOne.substring(0, updateOne.length() - 1); updateTwo += ")"; - verify(mockConnection) - .prepareStatement(updateOne); - verify(mockConnection) - .prepareStatement(updateTwo); + verify(mockConnection).prepareStatement(updateOne); + verify(mockConnection).prepareStatement(updateTwo); for (int i = 0; i < 500; i++) { verify(mockPreparedStatement).setLong(i + 1, (Long.valueOf(i))); } - for(int i = 500; i < 600; i++){ + for (int i = 500; i < 600; i++) { verify(mockPreparedStatement).setLong(i - 499, (Long.valueOf(i))); } - verify(mockPreparedStatement).close(); + verify(mockPreparedStatement, times(2)).close(); verify(mockConnection).close(); } @@ -1256,8 +1247,7 @@ public void ResetExpirationDate_FAIL() throws SQLException { // Assert Assertions.assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, data.getStatusCode()); Assertions.assertFalse(data.getBody(), "Success return on error"); - verify(mockConnection) - .prepareStatement("UPDATE ACTIVE_TIM SET EXPIRATION_DATE = NULL WHERE ACTIVE_TIM_ID IN (?)"); + verify(mockConnection).prepareStatement("UPDATE ACTIVE_TIM SET EXPIRATION_DATE = NULL WHERE ACTIVE_TIM_ID IN (?)"); verify(mockPreparedStatement).close(); verify(mockConnection).close(); } diff --git a/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/ActiveTimHoldingControllerTest.java b/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/ActiveTimHoldingControllerTest.java index 4c25d3929..4b7598675 100644 --- a/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/ActiveTimHoldingControllerTest.java +++ b/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/ActiveTimHoldingControllerTest.java @@ -5,6 +5,7 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; +import com.trihydro.library.model.ActiveTimHoldingDeleteModel; import java.math.BigDecimal; import java.sql.SQLException; import java.sql.Timestamp; @@ -25,232 +26,251 @@ import org.springframework.http.ResponseEntity; public class ActiveTimHoldingControllerTest extends TestBase { - @Spy - private TimDbTables mockTimDbTables = new TimDbTables(); - @Mock - private SQLNullHandler mockSqlNullHandler; - - private Coordinate startCoord; - private Coordinate endCoord; - - @BeforeEach - public void setupSubTest() { - uut.InjectDependencies(mockTimDbTables, mockSqlNullHandler); - startCoord = new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2)); - endCoord = new Coordinate(BigDecimal.valueOf(5), BigDecimal.valueOf(6)); - } - - private void setupInsertQueryStatement() { - doReturn("insert query statement").when(mockTimDbTables).buildInsertQueryStatement(any(), any()); - } - - private void setupPreparedStatement() throws SQLException { - doReturn(mockPreparedStatement).when(mockConnection).prepareStatement("insert query statement", - new String[] { "active_tim_holding_id" }); - } - - @Test - public void InsertActiveTimHolding_SUCCESS() throws SQLException { - // Arrange - setupInsertQueryStatement(); - setupPreparedStatement(); - ActiveTimHolding activeTimHolding = new ActiveTimHolding(); - activeTimHolding.setStartPoint(startCoord); - activeTimHolding.setEndPoint(endCoord); - activeTimHolding.setExpirationDateTime("2021-MAR-16'T'09:22'Z'"); - - var now = Instant.parse(activeTimHolding.getDateCreated()); - java.util.Date date_created = java.util.Date.from(now); - doReturn(date_created).when(mockUtility).convertDate(any()); - mockUtility.timestampFormat = timestampFormat; - Timestamp timestampDateCreated = new Timestamp(date_created.getTime()); - - // Act - ResponseEntity data = uut.InsertActiveTimHolding(activeTimHolding); - - // Assert - Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); - verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 2, activeTimHolding.getClientId());// CLIENT_ID - verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 3, activeTimHolding.getDirection());// DIRECTION - verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 4, activeTimHolding.getRsuTarget());// RSU_TARGET - verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 5, activeTimHolding.getSatRecordId());// SAT_RECORD_ID - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 6, - activeTimHolding.getStartPoint().getLatitude());// START_LATITUDE - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 7, - activeTimHolding.getStartPoint().getLongitude());// START_LONGITUDE - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 8, - activeTimHolding.getEndPoint().getLatitude());// END_LATITUDE - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 9, - activeTimHolding.getEndPoint().getLongitude());// END_LONGITUDE - verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 10, activeTimHolding.getRsuIndex());// RSU_INDEX - verify(mockSqlNullHandler).setTimestampOrNull(mockPreparedStatement, 11, timestampDateCreated);// DATE_CREATED - verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 12, - activeTimHolding.getProjectKey());// PROJECT_KEY - verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 13, - timestampFormat.format(date_created));// EXPIRATION_DATE - verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 14, activeTimHolding.getPacketId());// PACKET_ID - } - - @Test - public void InsertActiveTimHolding_ExistingSDX() throws SQLException { - // Arrange - setupInsertQueryStatement(); - setupPreparedStatement(); - ActiveTimHolding activeTimHolding = new ActiveTimHolding(); - activeTimHolding.setSatRecordId("satRecordId"); - activeTimHolding.setClientId("clientId"); - activeTimHolding.setDirection("direction"); - activeTimHolding.setStartPoint(startCoord); - activeTimHolding.setEndPoint(endCoord); - doReturn(null).when(mockDbInteractions).executeAndLog(mockPreparedStatement, "active tim holding"); - doReturn(-99l).when(mockRs).getLong("ACTIVE_TIM_HOLDING_ID"); - - var now = Instant.parse(activeTimHolding.getDateCreated()); - java.util.Date date_created = java.util.Date.from(now); - doReturn(date_created).when(mockUtility).convertDate(activeTimHolding.getDateCreated()); - mockUtility.timestampFormat = timestampFormat; - Timestamp timestampDateCreated = new Timestamp(date_created.getTime()); - - String query = "select active_tim_holding_id from active_tim_holding"; - query += " where sat_record_id = '" + activeTimHolding.getSatRecordId(); - query += "' and client_id = '" + activeTimHolding.getClientId(); - query += "' and direction = '" + activeTimHolding.getDirection() + "'"; - - // Act - ResponseEntity data = uut.InsertActiveTimHolding(activeTimHolding); - - // Assert - Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); - Assertions.assertEquals(Long.valueOf(-99), data.getBody()); - verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 2, activeTimHolding.getClientId());// CLIENT_ID - verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 3, activeTimHolding.getDirection());// DIRECTION - verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 4, activeTimHolding.getRsuTarget());// RSU_TARGET - verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 5, activeTimHolding.getSatRecordId());// SAT_RECORD_ID - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 6, - activeTimHolding.getStartPoint().getLatitude());// START_LATITUDE - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 7, - activeTimHolding.getStartPoint().getLongitude());// START_LONGITUDE - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 8, - activeTimHolding.getEndPoint().getLatitude());// END_LATITUDE - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 9, - activeTimHolding.getEndPoint().getLongitude());// END_LONGITUDE - verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 10, activeTimHolding.getRsuIndex());// RSU_INDEX - verify(mockSqlNullHandler).setTimestampOrNull(mockPreparedStatement, 11, timestampDateCreated);// DATE_CREATED - - verify(mockStatement).executeQuery(query); - } - - @Test - public void InsertActiveTimHolding_ExistingRSU() throws SQLException { - // Arrange - setupInsertQueryStatement(); - setupPreparedStatement(); - ActiveTimHolding activeTimHolding = new ActiveTimHolding(); - activeTimHolding.setRsuTargetId("10.10.10.1"); - activeTimHolding.setClientId("clientId"); - activeTimHolding.setDirection("direction"); - activeTimHolding.setStartPoint(startCoord); - activeTimHolding.setEndPoint(endCoord); - doReturn(null).when(mockDbInteractions).executeAndLog(mockPreparedStatement, "active tim holding"); - doReturn(-99l).when(mockRs).getLong("ACTIVE_TIM_HOLDING_ID"); - - var now = Instant.parse(activeTimHolding.getDateCreated()); - java.util.Date date_created = java.util.Date.from(now); - doReturn(date_created).when(mockUtility).convertDate(activeTimHolding.getDateCreated()); - mockUtility.timestampFormat = timestampFormat; - Timestamp timestampDateCreated = new Timestamp(date_created.getTime()); - - String query = "select active_tim_holding_id from active_tim_holding"; - query += " where rsu_target = '" + activeTimHolding.getRsuTarget(); - query += "' and client_id = '" + activeTimHolding.getClientId(); - query += "' and direction = '" + activeTimHolding.getDirection() + "'"; - - // Act - ResponseEntity data = uut.InsertActiveTimHolding(activeTimHolding); - - // Assert - Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); - Assertions.assertEquals(Long.valueOf(-99), data.getBody()); - verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 2, activeTimHolding.getClientId());// CLIENT_ID - verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 3, activeTimHolding.getDirection());// DIRECTION - verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 4, activeTimHolding.getRsuTarget());// RSU_TARGET - verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 5, activeTimHolding.getSatRecordId());// SAT_RECORD_ID - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 6, - activeTimHolding.getStartPoint().getLatitude());// START_LATITUDE - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 7, - activeTimHolding.getStartPoint().getLongitude());// START_LONGITUDE - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 8, - activeTimHolding.getEndPoint().getLatitude());// END_LATITUDE - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 9, - activeTimHolding.getEndPoint().getLongitude());// END_LONGITUDE - verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 10, activeTimHolding.getRsuIndex());// RSU_INDEX - verify(mockSqlNullHandler).setTimestampOrNull(mockPreparedStatement, 11, timestampDateCreated);// DATE_CREATED - - verify(mockStatement).executeQuery(query); - } - - @Test - public void InsertActiveTimHolding_FAIL() throws SQLException { - // Arrange - setupInsertQueryStatement(); - setupPreparedStatement(); - ActiveTimHolding activeTimHolding = new ActiveTimHolding(); - activeTimHolding.setStartPoint(startCoord); - activeTimHolding.setEndPoint(endCoord); - doThrow(new SQLException()).when(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 2, - activeTimHolding.getClientId()); - - // Act - ResponseEntity data = uut.InsertActiveTimHolding(activeTimHolding); - - // Assert - Assertions.assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, data.getStatusCode()); - verify(mockPreparedStatement).close(); - verify(mockConnection).close(); - - } - - @Test - public void getActiveTimHoldingForRsu_SUCCESS() throws SQLException { - // Arrange - - // Act - ResponseEntity> data = uut.getActiveTimHoldingForRsu("ipv4Address"); - - // Assert - Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); - Assertions.assertNotNull(data.getBody()); - Assertions.assertEquals(1, data.getBody().size()); - verify(mockRs).getLong("ACTIVE_TIM_HOLDING_ID"); - verify(mockRs).getString("CLIENT_ID"); - verify(mockRs).getString("DIRECTION"); - verify(mockRs).getString("RSU_TARGET"); - verify(mockRs).getString("SAT_RECORD_ID"); - verify(mockRs).getBigDecimal("START_LATITUDE"); - verify(mockRs).getBigDecimal("START_LONGITUDE"); - verify(mockRs).getBigDecimal("END_LATITUDE"); - verify(mockRs).getBigDecimal("END_LONGITUDE"); - verify(mockRs).getString("DATE_CREATED"); - verify(mockRs).getInt("RSU_INDEX"); - verify(mockStatement).close(); - verify(mockConnection).close(); - verify(mockRs).close(); - } - - @Test - public void getActiveTimHoldingForRsu_FAIL() throws SQLException { - // Arrange - doThrow(new SQLException()).when(mockRs).getLong("ACTIVE_TIM_HOLDING_ID"); - - // Act - ResponseEntity> data = uut.getActiveTimHoldingForRsu("ipv4Address"); - - // Assert - Assertions.assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, data.getStatusCode()); - Assertions.assertNotNull(data.getBody()); - Assertions.assertEquals(0, data.getBody().size()); - verify(mockStatement).close(); - verify(mockConnection).close(); - verify(mockRs).close(); - } + @Spy + private TimDbTables mockTimDbTables = new TimDbTables(); + @Mock + private SQLNullHandler mockSqlNullHandler; + + private Coordinate startCoord; + private Coordinate endCoord; + + @BeforeEach + public void setupSubTest() { + uut.InjectDependencies(mockTimDbTables, mockSqlNullHandler); + startCoord = new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2)); + endCoord = new Coordinate(BigDecimal.valueOf(5), BigDecimal.valueOf(6)); + } + + private void setupInsertQueryStatement() { + doReturn("insert query statement").when(mockTimDbTables).buildInsertQueryStatement(any(), any()); + } + + private void setupPreparedStatement() throws SQLException { + doReturn(mockPreparedStatement).when(mockConnection).prepareStatement("insert query statement", new String[] {"active_tim_holding_id"}); + } + + @Test + public void InsertActiveTimHolding_SUCCESS() throws SQLException { + // Arrange + setupInsertQueryStatement(); + setupPreparedStatement(); + ActiveTimHolding activeTimHolding = new ActiveTimHolding(); + activeTimHolding.setStartPoint(startCoord); + activeTimHolding.setEndPoint(endCoord); + activeTimHolding.setExpirationDateTime("2021-MAR-16'T'09:22'Z'"); + + var now = Instant.parse(activeTimHolding.getDateCreated()); + java.util.Date date_created = java.util.Date.from(now); + doReturn(date_created).when(mockUtility).convertDate(any()); + mockUtility.timestampFormat = timestampFormat; + Timestamp timestampDateCreated = new Timestamp(date_created.getTime()); + + // Act + ResponseEntity data = uut.InsertActiveTimHolding(activeTimHolding); + + // Assert + Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); + verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 2, activeTimHolding.getClientId());// CLIENT_ID + verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 3, activeTimHolding.getDirection());// DIRECTION + verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 4, activeTimHolding.getRsuTarget());// RSU_TARGET + verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 5, activeTimHolding.getSatRecordId());// SAT_RECORD_ID + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 6, activeTimHolding.getStartPoint().getLatitude());// START_LATITUDE + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 7, activeTimHolding.getStartPoint().getLongitude());// START_LONGITUDE + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 8, activeTimHolding.getEndPoint().getLatitude());// END_LATITUDE + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 9, activeTimHolding.getEndPoint().getLongitude());// END_LONGITUDE + verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 10, activeTimHolding.getRsuIndex());// RSU_INDEX + verify(mockSqlNullHandler).setTimestampOrNull(mockPreparedStatement, 11, timestampDateCreated);// DATE_CREATED + verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 12, activeTimHolding.getProjectKey());// PROJECT_KEY + verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 13, timestampFormat.format(date_created));// EXPIRATION_DATE + verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 14, activeTimHolding.getPacketId());// PACKET_ID + } + + @Test + public void InsertActiveTimHolding_ExistingSDX() throws SQLException { + // Arrange + setupInsertQueryStatement(); + setupPreparedStatement(); + ActiveTimHolding activeTimHolding = new ActiveTimHolding(); + activeTimHolding.setSatRecordId("satRecordId"); + activeTimHolding.setClientId("clientId"); + activeTimHolding.setDirection("direction"); + activeTimHolding.setStartPoint(startCoord); + activeTimHolding.setEndPoint(endCoord); + doReturn(null).when(mockDbInteractions).executeAndLog(mockPreparedStatement, "active tim holding"); + doReturn(-99l).when(mockRs).getLong("ACTIVE_TIM_HOLDING_ID"); + + var now = Instant.parse(activeTimHolding.getDateCreated()); + java.util.Date date_created = java.util.Date.from(now); + doReturn(date_created).when(mockUtility).convertDate(activeTimHolding.getDateCreated()); + mockUtility.timestampFormat = timestampFormat; + Timestamp timestampDateCreated = new Timestamp(date_created.getTime()); + + String query = "select active_tim_holding_id from active_tim_holding"; + query += " where sat_record_id = '" + activeTimHolding.getSatRecordId(); + query += "' and client_id = '" + activeTimHolding.getClientId(); + query += "' and direction = '" + activeTimHolding.getDirection() + "'"; + + // Act + ResponseEntity data = uut.InsertActiveTimHolding(activeTimHolding); + + // Assert + Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); + Assertions.assertEquals(Long.valueOf(-99), data.getBody()); + verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 2, activeTimHolding.getClientId());// CLIENT_ID + verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 3, activeTimHolding.getDirection());// DIRECTION + verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 4, activeTimHolding.getRsuTarget());// RSU_TARGET + verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 5, activeTimHolding.getSatRecordId());// SAT_RECORD_ID + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 6, activeTimHolding.getStartPoint().getLatitude());// START_LATITUDE + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 7, activeTimHolding.getStartPoint().getLongitude());// START_LONGITUDE + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 8, activeTimHolding.getEndPoint().getLatitude());// END_LATITUDE + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 9, activeTimHolding.getEndPoint().getLongitude());// END_LONGITUDE + verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 10, activeTimHolding.getRsuIndex());// RSU_INDEX + verify(mockSqlNullHandler).setTimestampOrNull(mockPreparedStatement, 11, timestampDateCreated);// DATE_CREATED + + verify(mockStatement).executeQuery(query); + } + + @Test + public void InsertActiveTimHolding_ExistingRSU() throws SQLException { + // Arrange + setupInsertQueryStatement(); + setupPreparedStatement(); + ActiveTimHolding activeTimHolding = new ActiveTimHolding(); + activeTimHolding.setRsuTargetId("10.10.10.1"); + activeTimHolding.setClientId("clientId"); + activeTimHolding.setDirection("direction"); + activeTimHolding.setStartPoint(startCoord); + activeTimHolding.setEndPoint(endCoord); + doReturn(null).when(mockDbInteractions).executeAndLog(mockPreparedStatement, "active tim holding"); + doReturn(-99l).when(mockRs).getLong("ACTIVE_TIM_HOLDING_ID"); + + var now = Instant.parse(activeTimHolding.getDateCreated()); + java.util.Date date_created = java.util.Date.from(now); + doReturn(date_created).when(mockUtility).convertDate(activeTimHolding.getDateCreated()); + mockUtility.timestampFormat = timestampFormat; + Timestamp timestampDateCreated = new Timestamp(date_created.getTime()); + + String query = "select active_tim_holding_id from active_tim_holding"; + query += " where rsu_target = '" + activeTimHolding.getRsuTarget(); + query += "' and client_id = '" + activeTimHolding.getClientId(); + query += "' and direction = '" + activeTimHolding.getDirection() + "'"; + + // Act + ResponseEntity data = uut.InsertActiveTimHolding(activeTimHolding); + + // Assert + Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); + Assertions.assertEquals(Long.valueOf(-99), data.getBody()); + verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 2, activeTimHolding.getClientId());// CLIENT_ID + verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 3, activeTimHolding.getDirection());// DIRECTION + verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 4, activeTimHolding.getRsuTarget());// RSU_TARGET + verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 5, activeTimHolding.getSatRecordId());// SAT_RECORD_ID + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 6, activeTimHolding.getStartPoint().getLatitude());// START_LATITUDE + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 7, activeTimHolding.getStartPoint().getLongitude());// START_LONGITUDE + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 8, activeTimHolding.getEndPoint().getLatitude());// END_LATITUDE + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 9, activeTimHolding.getEndPoint().getLongitude());// END_LONGITUDE + verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 10, activeTimHolding.getRsuIndex());// RSU_INDEX + verify(mockSqlNullHandler).setTimestampOrNull(mockPreparedStatement, 11, timestampDateCreated);// DATE_CREATED + + verify(mockStatement).executeQuery(query); + } + + @Test + public void InsertActiveTimHolding_FAIL() throws SQLException { + // Arrange + setupInsertQueryStatement(); + setupPreparedStatement(); + ActiveTimHolding activeTimHolding = new ActiveTimHolding(); + activeTimHolding.setStartPoint(startCoord); + activeTimHolding.setEndPoint(endCoord); + doThrow(new SQLException()).when(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 2, activeTimHolding.getClientId()); + + // Act + ResponseEntity data = uut.InsertActiveTimHolding(activeTimHolding); + + // Assert + Assertions.assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, data.getStatusCode()); + verify(mockPreparedStatement).close(); + verify(mockConnection).close(); + + } + + @Test + public void getActiveTimHoldingForRsu_SUCCESS() throws SQLException { + // Arrange + + // Act + ResponseEntity> data = uut.getActiveTimHoldingForRsu("ipv4Address"); + + // Assert + Assertions.assertEquals(HttpStatus.OK, data.getStatusCode()); + Assertions.assertNotNull(data.getBody()); + Assertions.assertEquals(1, data.getBody().size()); + verify(mockRs).getLong("ACTIVE_TIM_HOLDING_ID"); + verify(mockRs).getString("CLIENT_ID"); + verify(mockRs).getString("DIRECTION"); + verify(mockRs).getString("RSU_TARGET"); + verify(mockRs).getString("SAT_RECORD_ID"); + verify(mockRs).getBigDecimal("START_LATITUDE"); + verify(mockRs).getBigDecimal("START_LONGITUDE"); + verify(mockRs).getBigDecimal("END_LATITUDE"); + verify(mockRs).getBigDecimal("END_LONGITUDE"); + verify(mockRs).getString("DATE_CREATED"); + verify(mockRs).getInt("RSU_INDEX"); + verify(mockStatement).close(); + verify(mockConnection).close(); + verify(mockRs).close(); + } + + @Test + public void getActiveTimHoldingForRsu_FAIL() throws SQLException { + // Arrange + doThrow(new SQLException()).when(mockRs).getLong("ACTIVE_TIM_HOLDING_ID"); + + // Act + ResponseEntity> data = uut.getActiveTimHoldingForRsu("ipv4Address"); + + // Assert + Assertions.assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, data.getStatusCode()); + Assertions.assertNotNull(data.getBody()); + Assertions.assertEquals(0, data.getBody().size()); + verify(mockStatement).close(); + verify(mockConnection).close(); + verify(mockRs).close(); + } + + @Test + void getAllRecords_SuccessfulRetrieval_ShouldReturnRecords() throws SQLException { + // execute + ResponseEntity> response = uut.getAllRecords(); + + // verify + Assertions.assertEquals(HttpStatus.OK, response.getStatusCode()); + Assertions.assertNotNull(response.getBody()); + Assertions.assertEquals(1, response.getBody().size()); + verify(mockRs).getLong("ACTIVE_TIM_HOLDING_ID"); + verify(mockRs).getString("CLIENT_ID"); + verify(mockRs).getString("DIRECTION"); + verify(mockRs).getString("RSU_TARGET"); + verify(mockRs).getString("SAT_RECORD_ID"); + verify(mockRs).getBigDecimal("START_LATITUDE"); + verify(mockRs).getBigDecimal("START_LONGITUDE"); + verify(mockRs).getBigDecimal("END_LATITUDE"); + verify(mockRs).getBigDecimal("END_LONGITUDE"); + verify(mockRs).getString("DATE_CREATED"); + verify(mockRs).getInt("RSU_INDEX"); + verify(mockStatement).close(); + verify(mockConnection).close(); + verify(mockRs).close(); + } + + @Test + void deleteActiveTimHolding_SuccessfulExecution_ShouldReturnTrue() { + // execute + ResponseEntity response = uut.deleteActiveTimHoldingRecords(new ActiveTimHoldingDeleteModel(List.of(137L, 138L))); + + // verify + Assertions.assertNotNull(response.getBody()); + Assertions.assertTrue(response.getBody()); + } } \ No newline at end of file diff --git a/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/NodeXYControllerTest.java b/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/NodeXYControllerTest.java index a64953b23..4037489ff 100644 --- a/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/NodeXYControllerTest.java +++ b/cv-data-controller/src/test/java/com/trihydro/cvdatacontroller/controller/NodeXYControllerTest.java @@ -50,11 +50,11 @@ public void AddNodeXY_SUCCESS() throws SQLException { verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 1, nodeXY.getDelta());// DELTA verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 2, nodeXY.getNodeLat());// NODE_LAT verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 3, nodeXY.getNodeLong());// NODE_LONG - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 4, nodeXY.getX());// X - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 5, nodeXY.getY());// Y - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 6, nodeXY.getAttributes().getdWidth());// ATTRIBUTES_DWIDTH + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 4, nodeXY.getXpos());// X + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 5, nodeXY.getYpos());// Y + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 6, nodeXY.getAttributes().getDwidth());// ATTRIBUTES_DWIDTH verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 7, - nodeXY.getAttributes().getdElevation());// ATTRIBUTES_DELEVATION + nodeXY.getAttributes().getDelevation());// ATTRIBUTES_DELEVATION verify(mockPreparedStatement).close(); verify(mockConnection).close(); } diff --git a/cv-data-service-library/README.md b/cv-data-service-library/README.md index 3d87e4679..41b5b9b56 100644 Binary files a/cv-data-service-library/README.md and b/cv-data-service-library/README.md differ diff --git a/cv-data-service-library/pom.xml b/cv-data-service-library/pom.xml index 0b10b989d..1127a39b3 100644 --- a/cv-data-service-library/pom.xml +++ b/cv-data-service-library/pom.xml @@ -2,9 +2,9 @@ 4.0.0 - com.wyocv - wyo-cv - 1.4.0-SNAPSHOT + com.timm + tim-manager + 2.0.0 cv-data-service-library diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/exceptionhandlers/IdenticalPointsExceptionHandler.java b/cv-data-service-library/src/main/java/com/trihydro/library/exceptionhandlers/IdenticalPointsExceptionHandler.java new file mode 100644 index 000000000..e318da9f2 --- /dev/null +++ b/cv-data-service-library/src/main/java/com/trihydro/library/exceptionhandlers/IdenticalPointsExceptionHandler.java @@ -0,0 +1,49 @@ +package com.trihydro.library.exceptionhandlers; + +import com.trihydro.library.helpers.Utility; +import com.trihydro.library.model.Coordinate; +import com.trihydro.library.model.Milepost; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class IdenticalPointsExceptionHandler { + private final Utility utility; + + public IdenticalPointsExceptionHandler(Utility utility) { + this.utility = utility; + } + + /** + * Attempts to recover from an identical points exception by removing the first milepost + * and re-evaluating the remaining mileposts. If recovery is not possible due to insufficient + * mileposts or repeated identical points, returns null. + * + * @param allMps The list of Mileposts to process. The list must contain at least three mileposts + * to attempt recovery. + * @return The anchor point Milepost if recovery is successful, or null if recovery fails. + */ + public Milepost recover(List allMps) { + log.info("Attempting to recover from identical points exception"); + if (allMps.size() < 3) { + // if we only have 2 mileposts, we can't recover + log.warn( + "Unable to recover from identical points exception for active TIM, less than 3 mileposts found."); + return null; + } + // if we have more than 2 mileposts, we can remove the first milepost and try again + allMps.remove(0); + Milepost firstPoint = allMps.get(0); + Milepost secondPoint = allMps.get(1); + try { + Coordinate anchorCoordinate = utility.calculateAnchorCoordinate(firstPoint, secondPoint); + return new Milepost(null, firstPoint.getMilepost(), firstPoint.getDirection(), + anchorCoordinate.getLatitude(), anchorCoordinate.getLongitude()); + } catch (Utility.IdenticalPointsException e2) { + log.warn("Unable to recover from identical points exception for active TIM, first three mileposts are identical."); + return null; + } + } +} diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/CreateBaseTimUtil.java b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/CreateBaseTimUtil.java index 4b256573c..52174c1f5 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/CreateBaseTimUtil.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/CreateBaseTimUtil.java @@ -59,10 +59,6 @@ public WydotTravelerInputData buildTim(WydotTim wydotTim, TimGenerationProps gen // set TIM Properties OdeTravelerInformationMessage.DataFrame dataFrame = new OdeTravelerInformationMessage.DataFrame(); - dataFrame.setNotUsed((short) 0); // as of J2735 2020 this should be set to 0 and is ignored - dataFrame.setNotUsed1((short) 0); // as of J2735 2020 this should be set to 0 and is ignored - dataFrame.setNotUsed2((short) 0); // as of J2735 2020 this should be set to 0 and is ignored - dataFrame.setNotUsed3((short) 0); // as of J2735 2020 this should be set to 0 and is ignored // set TIM TimeStamp and StartDateTime to current time in UTC String nowAsISO = Instant.now().toString(); diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/DbInteractions.java b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/DbInteractions.java index a57d58920..1877fe2f6 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/DbInteractions.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/DbInteractions.java @@ -10,74 +10,48 @@ import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +/** + * The DbInteractions class is responsible for managing database interactions such as + * obtaining connections from a connection pool, executing SQL operations, and logging results. + * It facilitates configurations related to database connectivity and supports email alerts + * in case of operation failures. + *

+ * This class is annotated with {@code @Component} for Spring's component scanning and + * uses HikariCP for connection pooling. It depends on {@link DbInteractionsProps} for + * database configuration properties and {@link EmailHelper} for sending alert emails. + */ @Component +@Slf4j public class DbInteractions { private static HikariDataSource dataSource; - protected DbInteractionsProps dbConfig; - protected Utility utility; - private EmailHelper emailHelper; + private final DbInteractionsProps dbConfig; + private final EmailHelper emailHelper; @Autowired - public void InjectDependencies(DbInteractionsProps props, Utility _utility, EmailHelper _emailHelper) { - dbConfig = props; - utility = _utility; - emailHelper = _emailHelper; - utility.logWithDate("DbInteractions: Injecting dependencies"); - initDataSources(); - } - - private void initDataSources() { - if (dataSource == null) { - TimeZone timeZone = TimeZone.getTimeZone("America/Denver"); - TimeZone.setDefault(timeZone); - - // check dbconfig for null values - if (dbConfig.getDbUrl() == null || - dbConfig.getDbUsername() == null || - dbConfig.getDbPassword() == null || - dbConfig.getMaximumPoolSize() == 0 || - dbConfig.getConnectionTimeout() == 0) { - utility.logWithDate("DbInteractions: One or more database configuration values are undefined. Exiting."); - System.exit(1); - } - - // initialize connection pools - try { - initializePrimaryConnectionPool(); - } catch (Exception e) { - utility.logWithDate("DbInteractions: Failed to initialize primary connection pool due to unexpected exception:\n\n\"" + e.getMessage() + "\"\n"); - // e.printStackTrace(); - System.exit(1); - } - } - } - - private void initializePrimaryConnectionPool() { - HikariConfig config = new HikariConfig(); - config.setDriverClassName("org.postgresql.Driver"); - config.setJdbcUrl(dbConfig.getDbUrl()); - config.setUsername(dbConfig.getDbUsername()); - config.setPassword(dbConfig.getDbPassword()); - config.setConnectionTimeout(dbConfig.getConnectionTimeout()); - config.setMaximumPoolSize(dbConfig.getMaximumPoolSize()); - - // log the configuration of the connection pool - utility.logWithDate("DbInteractions: Creating connection pool with the following configuration:"); - utility.logWithDate("DbInteractions: driverClassName: " + config.getDriverClassName()); - utility.logWithDate("DbInteractions: dbUrl: " + dbConfig.getDbUrl()); - utility.logWithDate("DbInteractions: dbUsername: " + dbConfig.getDbUsername()); - utility.logWithDate("DbInteractions: connectionTimeout: " + config.getConnectionTimeout()); - utility.logWithDate("DbInteractions: maximumPoolSize: " + config.getMaximumPoolSize()); - - dataSource = new HikariDataSource(config); - utility.logWithDate("DbInteractions: Successfully initialized connection pool"); + public DbInteractions(DbInteractionsProps props, EmailHelper emailHelper) { + this.dbConfig = props; + this.emailHelper = emailHelper; + log.info("A new DbInteractions instance has been created."); + validateDbConfig(); } + /** + * Retrieves a connection from the connection pool. If the connection pool + * has not been initialized, it initializes the primary connection pool + * before retrieving a connection. + *

+ * In case of a failure in obtaining a connection, an email alert is sent + * to the configured alert addresses and a {@link SQLException} is thrown. + * + * @return A {@link Connection} instance from the connection pool. + * @throws SQLException If unable to retrieve a connection from the pool. + */ public Connection getConnectionPool() throws SQLException { // create pool if not already done if (dataSource == null) { @@ -97,62 +71,141 @@ public Connection getConnectionPool() throws SQLException { try { emailHelper.SendEmail(dbConfig.getAlertAddresses(), "Failed To Get Connection", body); } catch (Exception exception) { - utility.logWithDate("Failed to open connection to " + dbConfig.getDbUrl() - + ", then failed to send email"); - exception.printStackTrace(); + log.error("Failed to open connection to {}, then failed to send email", dbConfig.getDbUrl(), exception); } throw ex; } } + /** + * Executes the given PreparedStatement to perform an update or delete operation on the database. + * + * @param preparedStatement the PreparedStatement to execute for the update or delete operation + * @return true if the operation affected at least one row in the database, false otherwise + */ public boolean updateOrDelete(PreparedStatement preparedStatement) { - boolean result = false; - try { if (preparedStatement.executeUpdate() > 0) { result = true; } } catch (SQLException e) { - e.printStackTrace(); + log.error("Error in updateOrDelete", e); } - return result; } + /** + * Executes a delete operation using the provided PreparedStatement. The method + * returns a boolean indicating whether the operation was successful. If an + * exception occurs during execution, it is logged, and the method returns false. + * + * @param preparedStatement the PreparedStatement used to perform the delete operation + * @return true if the delete operation was successful, false if it failed + */ public boolean deleteWithPossibleZero(PreparedStatement preparedStatement) { boolean result = false; try { preparedStatement.executeUpdate(); result = true; } catch (SQLException e) { - e.printStackTrace(); + log.error("Error in deleteWithPossibleZero", e); } - return result; } + /** + * Executes the given prepared statement, logs the generated key if available, and returns the key. + * + * @param preparedStatement the prepared statement to be executed + * @param type the type of operation or entity being logged, for logging purposes + * @return the generated key as a Long if the execution is successful and a key is generated, otherwise null + */ public Long executeAndLog(PreparedStatement preparedStatement, String type) { Long id = null; try { if (preparedStatement.executeUpdate() > 0) { - ResultSet generatedKeys = preparedStatement.getGeneratedKeys(); - try { + try (ResultSet generatedKeys = preparedStatement.getGeneratedKeys()) { if (generatedKeys != null && generatedKeys.next()) { id = generatedKeys.getLong(1); - utility.logWithDate("------ Generated " + type + " " + id + " --------------"); - } - } finally { - try { - generatedKeys.close(); - } catch (Exception e) { - e.printStackTrace(); + log.trace("------ Generated {} {} --------------", type, id); } } } } catch (SQLException e) { - e.printStackTrace(); + log.error("Error in executeAndLog", e); } return id; } + + /** + * Validates the database configuration settings and ensures all necessary + * values are defined. If the configuration is invalid or incomplete, the + * application will log an error and terminate. + *

+ * Key actions performed by this method: + * - Checks if the `dataSource` is null, indicating a need to validate the configuration. + * - Sets the default application time zone to "America/Denver." + * - Verifies that all essential database configuration values (`dbUrl`, `dbUsername`, + * `dbPassword`, `maximumPoolSize`, and `connectionTimeout`) are defined and non-zero. + * - Logs an error and exits the program if any required configuration value is missing. + */ + private void validateDbConfig() { + if (dataSource != null) { + return; + } + TimeZone timeZone = TimeZone.getTimeZone("America/Denver"); + TimeZone.setDefault(timeZone); + + // check dbconfig for null values + if (dbConfig.getDbUrl() == null || + dbConfig.getDbUsername() == null || + dbConfig.getDbPassword() == null || + dbConfig.getMaximumPoolSize() == 0 || + dbConfig.getConnectionTimeout() == 0) { + log.error("One or more database configuration values are undefined. Exiting."); + System.exit(1); + } + } + + /** + * Initializes the primary connection pool using HikariCP for managing database connections. + * Configures the connection pool with properties retrieved from the database configuration. + * Logs the connection pool configuration details during initialization. + * On successful initialization, assigns the configured {@code HikariDataSource} to {@code dataSource}. + *

+ * This method is private and intended only for internal use by the class to create and configure + * the connection pool required for database operations. + *

+ * The connection pool is configured with the following settings: + * - Driver class name + * - JDBC URL + * - Database username + * - Database password + * - Connection timeout + * - Maximum pool size + *

+ * During the initialization process, any logging information regarding the + * pool's configuration is output through the configured logger. + */ + private void initializePrimaryConnectionPool() { + HikariConfig config = new HikariConfig(); + config.setDriverClassName("org.postgresql.Driver"); + config.setJdbcUrl(dbConfig.getDbUrl()); + config.setUsername(dbConfig.getDbUsername()); + config.setPassword(dbConfig.getDbPassword()); + config.setConnectionTimeout(dbConfig.getConnectionTimeout()); + config.setMaximumPoolSize(dbConfig.getMaximumPoolSize()); + + // log the configuration of the connection pool + log.info("Creating connection pool with the following configuration:"); + log.info("driverClassName: {}", config.getDriverClassName()); + log.info("dbUrl: {}", dbConfig.getDbUrl()); + log.info("dbUsername: {}", dbConfig.getDbUsername()); + log.info("connectionTimeout: {}", config.getConnectionTimeout()); + log.info("maximumPoolSize: {}", config.getMaximumPoolSize()); + + dataSource = new HikariDataSource(config); + log.info("Successfully initialized connection pool"); + } } \ No newline at end of file diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/JsonToJavaConverter.java b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/JsonToJavaConverter.java index aefc1fd61..1709b3751 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/JsonToJavaConverter.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/JsonToJavaConverter.java @@ -1,14 +1,18 @@ package com.trihydro.library.helpers; -import static java.time.temporal.TemporalAdjusters.firstDayOfYear; - import java.io.IOException; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; +import static java.time.temporal.TemporalAdjusters.firstDayOfYear; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import java.util.Map; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -17,8 +21,6 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.trihydro.library.model.ContentEnum; -import org.springframework.stereotype.Component; - import us.dot.its.jpo.ode.model.OdeLogMetadata; import us.dot.its.jpo.ode.model.OdeMsgMetadata.GeneratedBy; import us.dot.its.jpo.ode.model.OdeRequestMsgMetadata; @@ -28,9 +30,6 @@ import us.dot.its.jpo.ode.plugin.SNMP; import us.dot.its.jpo.ode.plugin.ServiceRequest; import us.dot.its.jpo.ode.plugin.SnmpProtocol; -import us.dot.its.jpo.ode.plugin.j2735.J2735SpecialVehicleExtensions; -import us.dot.its.jpo.ode.plugin.j2735.J2735SupplementalVehicleExtensions; -import us.dot.its.jpo.ode.plugin.j2735.J2735VehicleSafetyExtensions; import us.dot.its.jpo.ode.plugin.j2735.OdePosition3D; import us.dot.its.jpo.ode.plugin.j2735.OdeTravelerInformationMessage; import us.dot.its.jpo.ode.plugin.j2735.OdeTravelerInformationMessage.DataFrame.Region; @@ -40,45 +39,15 @@ import us.dot.its.jpo.ode.util.JsonUtils; @Component +@Slf4j public class JsonToJavaConverter { - private ObjectMapper mapper = new ObjectMapper(); + private final ObjectMapper mapper = new ObjectMapper(); public JsonToJavaConverter() { mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); } - public J2735SpecialVehicleExtensions convertJ2735SpecialVehicleExtensionsJsonToJava(String value, int i) { - - JsonNode part2Node = getPart2Node(value, i); - J2735SpecialVehicleExtensions spve = null; - try { - spve = mapper.treeToValue(part2Node, J2735SpecialVehicleExtensions.class); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } - return spve; - } - - public J2735SupplementalVehicleExtensions convertJ2735SupplementalVehicleExtensionsJsonToJava(String value, int i) { - - JsonNode part2Node = getPart2Node(value, i); - J2735SupplementalVehicleExtensions suve = null; - try { - suve = mapper.treeToValue(part2Node, J2735SupplementalVehicleExtensions.class); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } - return suve; - } - - public JsonNode getPart2Node(String value, int i) { - JsonNode part2 = JsonUtils.getJsonNode(value, "payload").get("data").get("partII"); - if (part2 != null) - return part2.get(i).get("value"); - return null; - } - public OdeLogMetadata convertTimMetadataJsonToJava(String value) { OdeLogMetadata odeTimMetadata = null; @@ -95,13 +64,12 @@ public OdeLogMetadata convertTimMetadataJsonToJava(String value) { ((ObjectNode) metaDataNode).replace("receivedMessageDetails", receivedMessageDetailsNode); } } - // System.out.println(metaDataNode); + log.trace("MetaDataNode: {}", metaDataNode); odeTimMetadata = mapper.treeToValue(metaDataNode, OdeLogMetadata.class); } catch (IOException e) { - System.out.println("IOException"); - System.out.println(e.getStackTrace()); + log.error("An IOException occurred while converting TIM metadata JSON to Java", e); } catch (NullPointerException e) { - System.out.println(e.getMessage()); + log.error("A NullPointerException occurred while converting TIM metadata JSON to Java: {}", e.getMessage()); } return odeTimMetadata; @@ -166,10 +134,10 @@ public OdeRequestMsgMetadata convertBroadcastTimMetadataJsonToJava(String value) } } catch (IOException e) { - System.out.println("IOException"); - System.out.println(e.getStackTrace()); + log.error("An IOException occurred while converting Broadcast TIM metadata JSON to Java", e); } catch (NullPointerException e) { - System.out.println(e.getMessage()); + log.error("A NullPointerException occurred while converting Broadcast TIM metadata JSON to Java: {}", + e.getMessage()); } return odeTimMetadata; } @@ -242,7 +210,14 @@ else if (reverse != null) } if (regionDirectionNode != null) { - region.setDirection(mapper.treeToValue(regionDirectionNode, String.class)); + StringBuilder directionBuilder = new StringBuilder(); + Iterator> fields = regionDirectionNode.fields(); + while (fields.hasNext()) { + Map.Entry field = fields.next(); + boolean directionBool= Boolean.parseBoolean(field.getValue().toString()); + directionBuilder.append(directionBool ? "1" : "0"); + } + region.setDirection(directionBuilder.toString()); } JsonNode descriptionNode = regionNode.get("description"); @@ -266,7 +241,7 @@ public OdeTimPayload convertTimPayloadJsonToJava(String value) { try { OdeTravelerInformationMessage.DataFrame[] dataFrames = new OdeTravelerInformationMessage.DataFrame[1]; OdeTravelerInformationMessage.DataFrame dataFrame = new OdeTravelerInformationMessage.DataFrame(); - List regions = new ArrayList(); + List regions = new ArrayList<>(); // JsonNode payloadNode = JsonUtils.getJsonNode(value, "payload"); JsonNode timNode = JsonUtils.getJsonNode(value, "payload").get("data").get("MessageFrame").get("value") @@ -352,7 +327,7 @@ else if (sequenceArrNode.get("item").get("text") != null) regions.add(region); } } else { - System.out.println("warning: geographicalPathNode is not an object or an array"); + log.warn("geographicalPathNode is not an object or an array"); } dataFrame.setRegions(regions.toArray(new OdeTravelerInformationMessage.DataFrame.Region[regions.size()])); @@ -362,9 +337,9 @@ else if (sequenceArrNode.get("item").get("text") != null) odeTimPayload = new OdeTimPayload(); odeTimPayload.setData(tim); } catch (IOException e) { - System.out.println(e.getStackTrace()); + log.error("An IOException occurred while converting TIM JSON to Java", e); } catch (NullPointerException e) { - System.out.println(e.getMessage()); + log.error("A NullPointerException occurred while converting TIM JSON to Java: {}", e.getMessage()); } return odeTimPayload; @@ -390,10 +365,10 @@ public OdeTravelerInformationMessage.DataFrame.Region.Path GetPathData(JsonNode if (nodesNode == null) return null; - JsonNode nodeXYArrNode = isXy ? nodesNode.get("NodeXY") : nodesNode.get("NodeLL"); + JsonNode nodeXYArrNode = isXy ? nodesNode.get("NodeXY") : nodesNode; OdeTravelerInformationMessage.DataFrame.Region.Path path = new OdeTravelerInformationMessage.DataFrame.Region.Path(); - List nodeXYs = new ArrayList(); - OdeTravelerInformationMessage.NodeXY nodeXY = new OdeTravelerInformationMessage.NodeXY(); + List nodeXYs = new ArrayList<>(); + OdeTravelerInformationMessage.NodeXY nodeXY; if (nodeXYArrNode.isArray()) { for (final JsonNode objNode : nodeXYArrNode) { @@ -470,133 +445,129 @@ public OdeTimPayload convertTmcTimTopicJsonToJava(String value) { OdeTimPayload odeTimPayload = null; try { - OdeTravelerInformationMessage.DataFrame[] dataFrames = new OdeTravelerInformationMessage.DataFrame[1]; + List dataFrames = new ArrayList<>(); OdeTravelerInformationMessage.DataFrame dataFrame = new OdeTravelerInformationMessage.DataFrame(); - List regions = new ArrayList(); - - // JsonNode timNode = JsonUtils.getJsonNode(value, - // "payload").get("data").get("MessageFrame").get("value") - // .get("TravelerInformation"); - JsonNode timNode = JsonUtils.getJsonNode(value, "payload").findValue("TravelerInformation"); - JsonNode travelerDataFrame = timNode.findValue("TravelerDataFrame"); - - JsonNode sequenceArrNode = null; - JsonNode contentNode = travelerDataFrame.get("content"); - if (contentNode.has(ContentEnum.advisory.getStringValue())) { - sequenceArrNode = contentNode.get(ContentEnum.advisory.getStringValue()).get("SEQUENCE"); - dataFrame.setContent(ContentEnum.advisory.getStringValue()); - } else if (contentNode.has(ContentEnum.speedLimit.getStringValue())) { - sequenceArrNode = contentNode.get(ContentEnum.speedLimit.getStringValue()).get("SEQUENCE"); - dataFrame.setContent(ContentEnum.speedLimit.getStringValue()); - } else if (contentNode.has(ContentEnum.exitService.getStringValue())) { - sequenceArrNode = contentNode.get(ContentEnum.exitService.getStringValue()).get("SEQUENCE"); - dataFrame.setContent(ContentEnum.exitService.getStringValue()); - } else if (contentNode.has(ContentEnum.genericSign.getStringValue())) { - sequenceArrNode = contentNode.get(ContentEnum.genericSign.getStringValue()).get("SEQUENCE"); - dataFrame.setContent(ContentEnum.genericSign.getStringValue()); - } else if (contentNode.has(ContentEnum.workZone.getStringValue())) { - sequenceArrNode = contentNode.get(ContentEnum.workZone.getStringValue()).get("SEQUENCE"); - dataFrame.setContent(ContentEnum.workZone.getStringValue()); - } - - List itemsList = new ArrayList(); - String item = null; - if (sequenceArrNode != null && sequenceArrNode.isArray()) { - for (final JsonNode objNode : sequenceArrNode) { - if (objNode.get("item").get("itis") != null) - item = mapper.treeToValue(objNode.get("item").get("itis"), String.class); - else if (objNode.get("item").get("text") != null) - item = mapper.treeToValue(objNode.get("item").get("text"), String.class); - - itemsList.add(item); - } - } - - // ADD NON ARRAY ELEMENT - if (sequenceArrNode != null && !sequenceArrNode.isArray()) { - if (sequenceArrNode.get("item").get("itis") != null) - item = mapper.treeToValue(sequenceArrNode.get("item").get("itis"), String.class); - else if (sequenceArrNode.get("item").get("text") != null) - item = mapper.treeToValue(sequenceArrNode.get("item").get("text"), String.class); + List regions = new ArrayList<>(); - itemsList.add(item); - } - - // TravelerInfoType.valueOf(); - JsonNode frameTypeNode = travelerDataFrame.get("frameType"); - if (frameTypeNode != null) { - if (frameTypeNode.fieldNames().hasNext()) { - TravelerInfoType frameType = TravelerInfoType.valueOf(frameTypeNode.fieldNames().next()); - if (frameType != null) { - dataFrame.setFrameType(frameType); - } - } + OdeTravelerInformationMessage tim = new OdeTravelerInformationMessage(); + JsonNode timNode = JsonUtils.getJsonNode(value, "payload").findValue("data"); + tim.setMsgCnt(timNode.get("msgCnt").asInt()); + JsonNode packetIDNode = timNode.get("packetID"); + if (packetIDNode != null) { + tim.setPacketID(packetIDNode.asText()); } - JsonNode startTimeNode = travelerDataFrame.get("startTime"); - JsonNode durationNode = travelerDataFrame.get("durationTime"); - JsonNode priorityNode = travelerDataFrame.get("priority"); - JsonNode notUsed1Node = travelerDataFrame.get("notUsed1"); - JsonNode notUsedNode = travelerDataFrame.get("notUsed"); - LocalDate now = LocalDate.now(); LocalDate firstDay = now.with(firstDayOfYear()); - OdeTravelerInformationMessage tim = new OdeTravelerInformationMessage(); - JsonNode timeStampNode = timNode.get("timeStamp"); if (timeStampNode != null) { LocalDateTime timeStampDate = firstDay.atStartOfDay().plus(timeStampNode.asInt(), ChronoUnit.MINUTES); tim.setTimeStamp(timeStampDate.toString() + "Z"); } - LocalDateTime startDate = firstDay.atStartOfDay().plus(startTimeNode.asInt(), ChronoUnit.MINUTES); + JsonNode travelerDataFrameArray = timNode.findValue("dataFrames"); + for (final JsonNode travelerDataFrame : travelerDataFrameArray) { + JsonNode sequenceArrNode = null; + JsonNode contentNode = travelerDataFrame.get("content"); + if (contentNode.has(ContentEnum.advisory.getStringValue())) { + sequenceArrNode = contentNode.get("advisory"); + dataFrame.setContent(ContentEnum.advisory.getStringValue()); + } else if (contentNode.has(ContentEnum.speedLimit.getStringValue())) { + sequenceArrNode = contentNode.get("speedLimit"); + dataFrame.setContent(ContentEnum.speedLimit.getStringValue()); + } else if (contentNode.has(ContentEnum.exitService.getStringValue())) { + sequenceArrNode = contentNode.get("exitService"); + dataFrame.setContent(ContentEnum.exitService.getStringValue()); + } else if (contentNode.has(ContentEnum.genericSign.getStringValue())) { + sequenceArrNode = contentNode.get("genericSign"); + dataFrame.setContent(ContentEnum.genericSign.getStringValue()); + } else if (contentNode.has(ContentEnum.workZone.getStringValue())) { + sequenceArrNode = contentNode.get("workZone"); + dataFrame.setContent(ContentEnum.workZone.getStringValue()); + } - dataFrame.setStartDateTime(startDate.toString() + "Z"); - dataFrame.setDurationTime(durationNode.asInt()); - dataFrame.setPriority(priorityNode.asInt()); - dataFrame.setNotUsed1((short) notUsed1Node.asInt()); - dataFrame.setNotUsed((short) notUsedNode.asInt()); + List itemsList = new ArrayList<>(); + String item = null; + if (sequenceArrNode != null && sequenceArrNode.isArray()) { + for (final JsonNode objNode : sequenceArrNode) { + if (objNode.get("item").get("itis") != null) { + item = mapper.treeToValue(objNode.get("item").get("itis"), String.class); + } else if (objNode.get("item").get("text") != null) { + item = mapper.treeToValue(objNode.get("item").get("text"), String.class); + } else { + log.warn("'itis' or 'text' not found in item when converting TMC TIM"); + } + if (!itemsList.contains(item)) { + itemsList.add(item); + } + } + } - tim.setMsgCnt(timNode.get("msgCnt").asInt()); + // ADD NON ARRAY ELEMENT + if (sequenceArrNode != null && !sequenceArrNode.isArray()) { + if (sequenceArrNode.get("item").get("itis") != null) { + item = mapper.treeToValue(sequenceArrNode.get("item").get("itis"), String.class); + } else if (sequenceArrNode.get("item").get("text") != null) { + item = mapper.treeToValue(sequenceArrNode.get("item").get("text"), String.class); + } else { + log.warn("'itis' or 'text' not found in item when converting TMC TIM"); + } - JsonNode packetIDNode = timNode.get("packetID"); - if (packetIDNode != null) { - tim.setPacketID(packetIDNode.asText()); - } + itemsList.add(item); + } - String[] items = new String[itemsList.size()]; - items = itemsList.toArray(items); + // TravelerInfoType.valueOf(); + JsonNode frameTypeNode = travelerDataFrame.get("frameType"); + if (frameTypeNode != null && frameTypeNode.fieldNames().hasNext()) { + TravelerInfoType frameType = TravelerInfoType.valueOf(frameTypeNode.fieldNames().next()); + dataFrame.setFrameType(frameType); + } else { + log.warn("frameType not found in TravelerDataFrame when converting TMC TIM. Defaulting to 'advisory'"); + dataFrame.setFrameType(TravelerInfoType.advisory); + } - JsonNode geographicalPathNode = travelerDataFrame.findValue("GeographicalPath"); + JsonNode startTimeNode = travelerDataFrame.get("startTime"); + JsonNode durationNode = travelerDataFrame.get("durationTime"); + JsonNode priorityNode = travelerDataFrame.get("priority"); - // geographicalPathNode may be an object or an array; if it is an object, treat - // it as a region - if (geographicalPathNode.isObject()) { - // single region - JsonNode regionNode = geographicalPathNode; - Region region = getRegion(regionNode); - regions.add(region); - } else if (geographicalPathNode.isArray()) { - // multiple regions - for (final JsonNode regionNode : geographicalPathNode) { - Region region = getRegion(regionNode); - regions.add(region); + LocalDateTime startDate = firstDay.atStartOfDay().plus(startTimeNode.asInt(), ChronoUnit.MINUTES); + + dataFrame.setStartDateTime(startDate.toString() + "Z"); + dataFrame.setDurationTime(durationNode.asInt()); + dataFrame.setPriority(priorityNode.asInt()); + + String[] items = new String[itemsList.size()]; + items = itemsList.toArray(items); + + JsonNode geographicalPathNode = travelerDataFrame.findValue("regions"); + + // geographicalPathNode may be an object or an array; if it is an object, treat + // it as a region + if (geographicalPathNode.isObject()) { + // single region + regions.add(getRegion(geographicalPathNode)); + } else if (geographicalPathNode.isArray()) { + // multiple regions + for (final JsonNode regionNode : geographicalPathNode) { + Region region = getRegion(regionNode); + regions.add(region); + } + } else { + log.warn("geographicalPathNode is not an object or an array"); } - } else { - System.out.println("warning: geographicalPathNode is not an object or an array"); - } - dataFrame.setRegions(regions.toArray(new OdeTravelerInformationMessage.DataFrame.Region[regions.size()])); - dataFrame.setItems(items); - dataFrames[0] = dataFrame; - tim.setDataframes(dataFrames); + dataFrame.setRegions(regions.toArray(OdeTravelerInformationMessage.DataFrame.Region[]::new)); + dataFrame.setItems(items); + dataFrames.add(dataFrame); + } + tim.setDataframes(dataFrames.toArray(OdeTravelerInformationMessage.DataFrame[]::new)); odeTimPayload = new OdeTimPayload(); odeTimPayload.setData(tim); } catch (IOException e) { - System.out.println(e.getStackTrace()); + log.error("An IOException occurred while converting TMC TIM JSON to Java", e); } catch (NullPointerException e) { - System.out.println(e.getMessage()); + log.error("A NullPointerException occurred while converting TMC TIM JSON to Java: {}", e.getMessage()); } return odeTimPayload; @@ -610,9 +581,9 @@ public OdeTravelerInformationMessage convertBroadcastTimPayloadJsonToJava(String JsonNode timNode = JsonUtils.getJsonNode(value, "payload").get("data"); odeTim = mapper.treeToValue(timNode, OdeTravelerInformationMessage.class); } catch (IOException e) { - System.out.println(e.getStackTrace()); + log.error("An IOException occurred while converting Broadcast TIM JSON to Java", e); } catch (NullPointerException e) { - System.out.println(e.getMessage()); + log.error("A NullPointerException occurred while converting Broadcast TIM JSON to Java: {}", e.getMessage()); } return odeTim; diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/TimGenerationHelper.java b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/TimGenerationHelper.java index d763367f9..f88afdebd 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/TimGenerationHelper.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/TimGenerationHelper.java @@ -1,5 +1,6 @@ package com.trihydro.library.helpers; +import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; import java.math.BigDecimal; import java.sql.Timestamp; import java.text.DateFormat; @@ -35,11 +36,10 @@ import com.trihydro.library.service.DataFrameService; import com.trihydro.library.service.MilepostService; import com.trihydro.library.service.OdeService; -import com.trihydro.library.service.PathNodeLLService; import com.trihydro.library.service.RsuService; -import com.trihydro.library.service.SdwService; import com.trihydro.library.service.TimGenerationProps; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -62,28 +62,31 @@ import us.dot.its.jpo.ode.plugin.j2735.timstorage.MutcdCode.MutcdCodeEnum; @Component +@Slf4j public class TimGenerationHelper { - private Utility utility; - private DataFrameService dataFrameService; - private ActiveTimService activeTimService; - private MilepostService milepostService; - private Gson gson; - private MilepostReduction milepostReduction; - private RsuService rsuService; - private TimGenerationProps config; - private OdeService odeService; - private ActiveTimHoldingService activeTimHoldingService; - private SnmpHelper snmpHelper; - private RegionNameTrimmer regionNameTrimmer; - private CreateBaseTimUtil createBaseTimUtil; + private final Utility utility; + private final DataFrameService dataFrameService; + private final ActiveTimService activeTimService; + private final MilepostService milepostService; + private final Gson gson; + private final MilepostReduction milepostReduction; + private final RsuService rsuService; + private final TimGenerationProps config; + private final OdeService odeService; + private final ActiveTimHoldingService activeTimHoldingService; + private final SnmpHelper snmpHelper; + private final RegionNameTrimmer regionNameTrimmer; + private final CreateBaseTimUtil createBaseTimUtil; + private final IdenticalPointsExceptionHandler identicalPointsExceptionHandler; @Autowired public TimGenerationHelper(Utility _utility, DataFrameService _dataFrameService, - PathNodeLLService _pathNodeLLService, ActiveTimService _activeTimService, MilepostService _milepostService, - MilepostReduction _milepostReduction, TimGenerationProps _config, RsuService _rsuService, - OdeService _odeService, ActiveTimHoldingService _activeTimHoldingService, SdwService _sdwService, - SnmpHelper _snmpHelper, RegionNameTrimmer _regionNameTrimmer, - CreateBaseTimUtil _createBaseTimUtil) { + ActiveTimService _activeTimService, MilepostService _milepostService, + MilepostReduction _milepostReduction, TimGenerationProps _config, + RsuService _rsuService, OdeService _odeService, + ActiveTimHoldingService _activeTimHoldingService, + SnmpHelper _snmpHelper, RegionNameTrimmer _regionNameTrimmer, + CreateBaseTimUtil _createBaseTimUtil, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { gson = new Gson(); utility = _utility; dataFrameService = _dataFrameService; @@ -97,6 +100,7 @@ public TimGenerationHelper(Utility _utility, DataFrameService _dataFrameService, snmpHelper = _snmpHelper; regionNameTrimmer = _regionNameTrimmer; createBaseTimUtil = _createBaseTimUtil; + this.identicalPointsExceptionHandler = identicalPointsExceptionHandler; } private String getIsoDateTimeString(ZonedDateTime date) { @@ -118,11 +122,11 @@ private String getIsoDateTimeString(Timestamp time) { return formatter.format(time); } - public List updateAndResubmitToOde(List validationResults) { + public List updateAndResubmitToOde( + List validationResults) { List exceptions = new ArrayList<>(); - if (validationResults == null || validationResults.size() == 0) { - utility.logWithDate("No validation results found to update and resubmit to ODE.", - TimGenerationHelper.class); + if (validationResults == null || validationResults.isEmpty()) { + log.info("No validation results found to update and resubmit to ODE."); return exceptions; } // iterate over tims, fetch, and push out @@ -132,12 +136,13 @@ public List updateAndResubmitToOde(List updateAndResubmitToOde(List mps = new ArrayList<>(); + List mps; List allMps = getAllMps(wydotTim); if (allMps.size() < 2) { String exMsg = String.format( - "Unable to resubmit TIM, less than 2 mileposts found to determine service area for Active_Tim %d", - tum.getActiveTimId()); - utility.logWithDate(exMsg); + "Unable to resubmit TIM, less than 2 mileposts found to determine service area for Active_Tim %d", + tum.getActiveTimId()); + log.error(exMsg); exceptions.add(new ResubmitTimException(activeTimId, exMsg)); continue; } + Milepost firstPoint = allMps.get(0); Milepost secondPoint = allMps.get(1); + Milepost anchorMp; + try { + Coordinate anchorCoordinate = utility.calculateAnchorCoordinate(firstPoint, secondPoint); + anchorMp = new Milepost(null, firstPoint.getMilepost(), firstPoint.getDirection(), anchorCoordinate.getLatitude(), + anchorCoordinate.getLongitude()); + } catch (Utility.IdenticalPointsException e) { + anchorMp = identicalPointsExceptionHandler.recover(allMps); + if (anchorMp == null) { + String exMsg = String.format( + "Unable to resubmit TIM, identical points found while calculating anchor point for Active_Tim %d", + tum.getActiveTimId()); + log.error(exMsg, e); + exceptions.add(new ResubmitTimException(tum.getActiveTimId(), exMsg)); + continue; + } + } + // reduce the mileposts by removing straight away posts - var anchorMp = getAnchorPoint(firstPoint, secondPoint); - mps = milepostReduction.applyMilepostReductionAlgorithm(allMps, config.getPathDistanceLimit()); + mps = milepostReduction.applyMilepostReductionAlgorithm(allMps, + config.getPathDistanceLimit()); OdeTravelerInformationMessage tim = getTim(tum, mps, allMps, anchorMp, false); if (tim == null) { String exMsg = String.format("Failed to instantiate TIM for active_tim_id %d", - tum.getActiveTimId()); - utility.logWithDate(exMsg); + tum.getActiveTimId()); + log.error(exMsg); exceptions.add(new ResubmitTimException(activeTimId, exMsg)); continue; } @@ -178,33 +201,35 @@ public List updateAndResubmitToOde(List 0) { - utility.logWithDate("Unable to update TIM (active_tim_id " + tum.getActiveTimId() - + ") with validationResult: " + gson.toJson(validationResult)); + if (tim == null || !exceptions.isEmpty()) { + log.error( + "Failed to update TIM for active_tim_id {} with ActiveTimValidationResult: {}", + tum.getActiveTimId(), gson.toJson(validationResult)); continue; } WydotTravelerInputData timToSend = new WydotTravelerInputData(); timToSend.setTim(tim); var extraEx = sendTim(timToSend, tum, activeTimId, mps); - if (extraEx.size() > 0) { + if (!extraEx.isEmpty()) { exceptions.addAll(extraEx); } } catch (Exception ex) { - utility.logWithDate("Failed attempting to update TIM (active_tim_id " - + validationResult.getActiveTim().getActiveTimId() + ") with ActiveTimValidationResult: " - + gson.toJson(validationResult)); - ex.printStackTrace(); + log.error( + "Failed to update TIM (active_tim_id {}) with ActiveTimValidationResult: {}", + validationResult.getActiveTim().getActiveTimId(), gson.toJson(validationResult), + ex); } } - if (exceptions.size() > 0) { - utility.logWithDate("Errors occurred while resubmitting TIMs: " + gson.toJson(exceptions), - TimGenerationHelper.class); + if (!exceptions.isEmpty()) { + log.error("Errors occurred while resubmitting TIMs: {}", gson.toJson(exceptions)); } return exceptions; } - private OdeTravelerInformationMessage updateTim(TimUpdateModel tum, OdeTravelerInformationMessage tim, - ActiveTimValidationResult validationResult, List exceptions) { + private OdeTravelerInformationMessage updateTim(TimUpdateModel tum, + OdeTravelerInformationMessage tim, + ActiveTimValidationResult validationResult, + List exceptions) { DateFormat dFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MMM-yy HH.MM.SS"); @@ -226,7 +251,8 @@ private OdeTravelerInformationMessage updateTim(TimUpdateModel tum, OdeTravelerI // FEU is text formatted as 2020-12-08 09:31:00 try { Date endDateTime = dFormat.parse(ate.getTmddValue()); - int durationTime = utility.getMinutesDurationBetweenTwoDates(tum.getStartDateTime(), + int durationTime = + utility.getMinutesDurationBetweenTwoDates(tum.getStartDateTime(), simpleDateFormat.format(endDateTime)); // J2735 has duration time of 0-32000 // the ODE fails if we have greater than 32000 @@ -235,8 +261,9 @@ private OdeTravelerInformationMessage updateTim(TimUpdateModel tum, OdeTravelerI } tim.getDataframes()[0].setDurationTime(durationTime); } catch (ParseException e) { - String exMsg = String.format("Failed to parse associated FEU date: %s", ate.getTmddValue()); - utility.logWithDate(exMsg); + String exMsg = String.format("Failed to parse associated FEU date: %s", + ate.getTmddValue()); + log.error(exMsg, e); exceptions.add(new ResubmitTimException(tum.getActiveTimId(), exMsg)); } break; @@ -272,26 +299,41 @@ private OdeTravelerInformationMessage updateTim(TimUpdateModel tum, OdeTravelerI // rebuild tim...this should maybe be extracted to another function and called // from multiple locations WydotTim wydotTim = getWydotTimFromTum(tum); - List mps = new ArrayList<>(); List allMps = getAllMps(wydotTim); if (allMps.size() < 2) { String exMsg = String.format( - "Unable to resubmit TIM, less than 2 mileposts found to determine service area for Active_Tim %d", - tum.getActiveTimId()); - utility.logWithDate(exMsg); + "Unable to resubmit TIM, less than 2 mileposts found to determine service area for Active_Tim %d", + tum.getActiveTimId()); + log.error(exMsg); exceptions.add(new ResubmitTimException(tum.getActiveTimId(), exMsg)); return null; } Milepost firstPoint = allMps.get(0); Milepost secondPoint = allMps.get(1); - // reduce the mileposts by removing straight away posts - var anchorMp = getAnchorPoint(firstPoint, secondPoint); - mps = milepostReduction.applyMilepostReductionAlgorithm(allMps, config.getPathDistanceLimit()); + Milepost anchorMp; + try { + Coordinate anchorCoordinate = utility.calculateAnchorCoordinate(firstPoint, secondPoint); + anchorMp = new Milepost(null, firstPoint.getMilepost(), firstPoint.getDirection(), anchorCoordinate.getLatitude(), + anchorCoordinate.getLongitude()); + } catch (Utility.IdenticalPointsException e) { + anchorMp = identicalPointsExceptionHandler.recover(allMps); + if (anchorMp == null) { + String exMsg = String.format( + "Unable to resubmit TIM, identical points found while calculating anchor point for Active_Tim %d", + tum.getActiveTimId()); + log.error(exMsg, e); + exceptions.add(new ResubmitTimException(tum.getActiveTimId(), exMsg)); + return null; + } + } + List mps = milepostReduction.applyMilepostReductionAlgorithm(allMps, + config.getPathDistanceLimit()); tim = getTim(tum, mps, allMps, anchorMp, false); if (tim == null) { - String exMsg = String.format("Failed to instantiate TIM for active_tim_id %d", tum.getActiveTimId()); - utility.logWithDate(exMsg); + String exMsg = String.format("Failed to instantiate TIM for active_tim_id %d", + tum.getActiveTimId()); + log.error(exMsg); exceptions.add(new ResubmitTimException(tum.getActiveTimId(), exMsg)); return null; } @@ -310,13 +352,13 @@ private WydotTim getWydotTimFromTum(TimUpdateModel tum) { } private List getAllMps(WydotTim wydotTim) { - List allMps = new ArrayList<>(); + List allMps; - utility.logWithDate("Fetching mileposts for regular TIM with client id: " + wydotTim.getClientId()); + log.info("Fetching mileposts for regular TIM with client id: {}", wydotTim.getClientId()); if (wydotTim.getEndPoint() != null) { allMps = milepostService.getMilepostsByStartEndPointDirection(wydotTim); - utility.logWithDate(String.format("Found %d mileposts between %s and %s", allMps.size(), - gson.toJson(wydotTim.getStartPoint()), gson.toJson(wydotTim.getEndPoint()))); + log.info("Found {} mileposts between {} and {}", allMps.size(), + gson.toJson(wydotTim.getStartPoint()), gson.toJson(wydotTim.getEndPoint())); } else { // point incident MilepostBuffer mpb = new MilepostBuffer(); @@ -325,8 +367,8 @@ private List getAllMps(WydotTim wydotTim) { mpb.setDirection(wydotTim.getDirection()); mpb.setPoint(wydotTim.getStartPoint()); allMps = milepostService.getMilepostsByPointWithBuffer(mpb); - utility.logWithDate(String.format("Found %d mileposts for point %s", allMps.size(), - gson.toJson(wydotTim.getStartPoint()))); + log.info("Found {} mileposts for point {}", allMps.size(), + gson.toJson(wydotTim.getStartPoint())); } return allMps; @@ -334,14 +376,14 @@ private List getAllMps(WydotTim wydotTim) { /** * Rebuilds and resubmits TIMs to the ODE - * + * * @param activeTimIds TIMs to resubmit * @return Errors that occurred while processing the request */ public List resubmitToOde(List activeTimIds) { List exceptions = new ArrayList<>(); if (activeTimIds == null) { - utility.logWithDate("No active TIMs found to resubmit to ODE. Returning...", TimGenerationHelper.class); + log.info("No active TIMs found to resubmit to ODE. Returning..."); return exceptions; } // iterate over tims, fetch, and push out @@ -350,12 +392,13 @@ public List resubmitToOde(List activeTimIds) { var tum = activeTimService.getUpdateModelFromActiveTimId(activeTimId); if (tum == null) { - exceptions.add(new ResubmitTimException(activeTimId, "Failed to get Update Model from active tim")); + exceptions.add(new ResubmitTimException(activeTimId, + "Failed to get Update Model from active tim")); continue; } if (!isValidTim(tum)) { exceptions.add(new ResubmitTimException(activeTimId, - "Failed to generate valid Update Model from active tim")); + "Failed to generate valid Update Model from active tim")); continue; } @@ -371,53 +414,71 @@ public List resubmitToOde(List activeTimIds) { List allMps = getAllMps(wydotTim); if (allMps.size() < 2) { String exMsg = String.format( - "Unable to resubmit TIM, less than 2 mileposts found for Active_Tim %d", - tum.getActiveTimId()); - utility.logWithDate(exMsg); + "Unable to resubmit TIM, less than 2 mileposts found for Active_Tim %d", + tum.getActiveTimId()); + log.error(exMsg); exceptions.add(new ResubmitTimException(activeTimId, exMsg)); continue; } + Milepost firstPoint = allMps.get(0); Milepost secondPoint = allMps.get(1); + Milepost anchorMp; + try { + Coordinate anchorCoordinate = utility.calculateAnchorCoordinate(firstPoint, secondPoint); + anchorMp = new Milepost(null, firstPoint.getMilepost(), firstPoint.getDirection(), anchorCoordinate.getLatitude(), + anchorCoordinate.getLongitude()); + } catch (Utility.IdenticalPointsException e) { + anchorMp = identicalPointsExceptionHandler.recover(allMps); + if (anchorMp == null) { + String exMsg = String.format( + "Unable to resubmit TIM, identical points found while calculating anchor point for Active_Tim %d", + tum.getActiveTimId()); + log.error(exMsg, e); + exceptions.add(new ResubmitTimException(tum.getActiveTimId(), exMsg)); + continue; + } + } + // reduce the mileposts by removing straight away posts - var anchorMp = getAnchorPoint(firstPoint, secondPoint); - reduced_mps = milepostReduction.applyMilepostReductionAlgorithm(allMps, config.getPathDistanceLimit()); - OdeTravelerInformationMessage tim = getTim(tum, reduced_mps, allMps, anchorMp, false, false); + reduced_mps = milepostReduction.applyMilepostReductionAlgorithm(allMps, + config.getPathDistanceLimit()); + OdeTravelerInformationMessage tim = + getTim(tum, reduced_mps, allMps, anchorMp, false, false); if (tim == null) { String exMsg = String.format("Failed to instantiate TIM for active_tim_id %d", - tum.getActiveTimId()); - utility.logWithDate(exMsg); + tum.getActiveTimId()); + log.error(exMsg); exceptions.add(new ResubmitTimException(activeTimId, exMsg)); continue; } WydotTravelerInputData timToSend = new WydotTravelerInputData(); timToSend.setTim(tim); var extraEx = sendTim(timToSend, tum, activeTimId, reduced_mps); - if (extraEx.size() > 0) { + if (!extraEx.isEmpty()) { exceptions.addAll(extraEx); } } catch (Exception ex) { exceptions.add(new ResubmitTimException(activeTimId, ex.getMessage())); } } - if (exceptions.size() > 0) { - utility.logWithDate("Errors occurred while resubmitting TIMs: " + gson.toJson(exceptions), - TimGenerationHelper.class); + if (!exceptions.isEmpty()) { + log.error("Errors occurred while resubmitting TIMs: {}", gson.toJson(exceptions)); } return exceptions; } /** * Resets TIMs starting time and resubmits TIMs to the ODE - * + * * @param activeTimIds TIMs to resubmit * @return Errors that occurred while processing the request */ public List resetTimStartTimeAndResubmitToOde(List activeTimIds) { List exceptions = new ArrayList<>(); if (activeTimIds == null) { - utility.logWithDate("No active TIMs found to resubmit to ODE. Returning...", TimGenerationHelper.class); + log.info("No active TIMs found to resubmit to ODE. Returning..."); return exceptions; } // iterate over tims, fetch, and push out @@ -426,12 +487,13 @@ public List resetTimStartTimeAndResubmitToOde(List a var tum = activeTimService.getUpdateModelFromActiveTimId(activeTimId); if (tum == null) { - exceptions.add(new ResubmitTimException(activeTimId, "Failed to get Update Model from active tim")); + exceptions.add(new ResubmitTimException(activeTimId, + "Failed to get Update Model from active tim")); continue; } if (!isValidTim(tum)) { exceptions.add(new ResubmitTimException(activeTimId, - "Failed to generate valid Update Model from active tim")); + "Failed to generate valid Update Model from active tim")); continue; } @@ -447,54 +509,71 @@ public List resetTimStartTimeAndResubmitToOde(List a List allMps = getAllMps(wydotTim); if (allMps.size() < 2) { String exMsg = String.format( - "Unable to resubmit TIM, less than 2 mileposts found for Active_Tim %d", - tum.getActiveTimId()); - utility.logWithDate(exMsg); + "Unable to resubmit TIM, less than 2 mileposts found for Active_Tim %d", + tum.getActiveTimId()); + log.error(exMsg); exceptions.add(new ResubmitTimException(activeTimId, exMsg)); continue; } + Milepost firstPoint = allMps.get(0); Milepost secondPoint = allMps.get(1); + Milepost anchorMp; + try { + Coordinate anchorCoordinate = utility.calculateAnchorCoordinate(firstPoint, secondPoint); + anchorMp = new Milepost(null, firstPoint.getMilepost(), firstPoint.getDirection(), anchorCoordinate.getLatitude(), + anchorCoordinate.getLongitude()); + } catch (Utility.IdenticalPointsException e) { + anchorMp = identicalPointsExceptionHandler.recover(allMps); + if (anchorMp == null) { + String exMsg = String.format( + "Unable to resubmit TIM, identical points found while calculating anchor point for Active_Tim %d", + tum.getActiveTimId()); + log.error(exMsg, e); + exceptions.add(new ResubmitTimException(tum.getActiveTimId(), exMsg)); + continue; + } + } + // reduce the mileposts by removing straight away posts - var anchorMp = getAnchorPoint(firstPoint, secondPoint); - reduced_mps = milepostReduction.applyMilepostReductionAlgorithm(allMps, config.getPathDistanceLimit()); - OdeTravelerInformationMessage tim = getTim(tum, reduced_mps, allMps, anchorMp, true, false); + reduced_mps = milepostReduction.applyMilepostReductionAlgorithm(allMps, + config.getPathDistanceLimit()); + OdeTravelerInformationMessage tim = + getTim(tum, reduced_mps, allMps, anchorMp, true, false); if (tim == null) { String exMsg = String.format("Failed to instantiate TIM for active_tim_id %d", - tum.getActiveTimId()); - utility.logWithDate(exMsg); + tum.getActiveTimId()); + log.error(exMsg); exceptions.add(new ResubmitTimException(activeTimId, exMsg)); continue; } WydotTravelerInputData timToSend = new WydotTravelerInputData(); timToSend.setTim(tim); var extraEx = sendTim(timToSend, tum, activeTimId, reduced_mps); - if (extraEx.size() > 0) { + if (!extraEx.isEmpty()) { exceptions.addAll(extraEx); } } catch (Exception ex) { exceptions.add(new ResubmitTimException(activeTimId, ex.getMessage())); } } - if (exceptions.size() > 0) { - utility.logWithDate("Errors occurred while resubmitting TIMs: " + gson.toJson(exceptions), - TimGenerationHelper.class); + if (!exceptions.isEmpty()) { + log.error("Errors occurred while resubmitting TIMs: {}", gson.toJson(exceptions)); } return exceptions; } /** * Expires existing TIMs and resubmits TIMs to the ODE - * + * * @param activeTimIds TIMs to resubmit - * @return Errors that occurred while processing the request */ - public List expireTimAndResubmitToOde(List activeTimIds) { + public void expireTimAndResubmitToOde(List activeTimIds) { List exceptions = new ArrayList<>(); if (activeTimIds == null) { - utility.logWithDate("No active TIMs found to resubmit to ODE. Returning...", TimGenerationHelper.class); - return exceptions; + log.info("No active TIMs found to resubmit to ODE. Returning..."); + return; } // iterate over tims, fetch, and push out for (Long activeTimId : activeTimIds) { @@ -502,12 +581,13 @@ public List expireTimAndResubmitToOde(List activeTim var tum = activeTimService.getUpdateModelFromActiveTimId(activeTimId); if (tum == null) { - exceptions.add(new ResubmitTimException(activeTimId, "Failed to get Update Model from active tim")); + exceptions.add(new ResubmitTimException(activeTimId, + "Failed to get Update Model from active tim")); continue; } if (!isValidTim(tum)) { exceptions.add(new ResubmitTimException(activeTimId, - "Failed to generate valid Update Model from active tim")); + "Failed to generate valid Update Model from active tim")); continue; } @@ -523,24 +603,43 @@ public List expireTimAndResubmitToOde(List activeTim List allMps = getAllMps(wydotTim); if (allMps.size() < 2) { String exMsg = String.format( - "Unable to resubmit TIM, less than 2 mileposts found for Active_Tim %d", - tum.getActiveTimId()); - utility.logWithDate(exMsg); + "Unable to resubmit TIM, less than 2 mileposts found for Active_Tim %d", + tum.getActiveTimId()); + log.error(exMsg); exceptions.add(new ResubmitTimException(activeTimId, exMsg)); continue; } + Milepost firstPoint = allMps.get(0); Milepost secondPoint = allMps.get(1); + Milepost anchorMp; + try { + Coordinate anchorCoordinate = utility.calculateAnchorCoordinate(firstPoint, secondPoint); + anchorMp = new Milepost(null, firstPoint.getMilepost(), firstPoint.getDirection(), anchorCoordinate.getLatitude(), + anchorCoordinate.getLongitude()); + } catch (Utility.IdenticalPointsException e) { + anchorMp = identicalPointsExceptionHandler.recover(allMps); + if (anchorMp == null) { + String exMsg = String.format( + "Unable to resubmit TIM, identical points found while calculating anchor point for Active_Tim %d", + tum.getActiveTimId()); + log.error(exMsg, e); + exceptions.add(new ResubmitTimException(tum.getActiveTimId(), exMsg)); + continue; + } + } + // reduce the mileposts by removing straight away posts - var anchorMp = getAnchorPoint(firstPoint, secondPoint); - reduced_mps = milepostReduction.applyMilepostReductionAlgorithm(allMps, config.getPathDistanceLimit()); + reduced_mps = milepostReduction.applyMilepostReductionAlgorithm(allMps, + config.getPathDistanceLimit()); - OdeTravelerInformationMessage tim = getTim(tum, reduced_mps, allMps, anchorMp, false, true); + OdeTravelerInformationMessage tim = + getTim(tum, reduced_mps, allMps, anchorMp, false, true); if (tim == null) { String exMsg = String.format("Failed to instantiate TIM for active_tim_id %d", - tum.getActiveTimId()); - utility.logWithDate(exMsg); + tum.getActiveTimId()); + log.error(exMsg); exceptions.add(new ResubmitTimException(activeTimId, exMsg)); continue; } @@ -548,62 +647,69 @@ public List expireTimAndResubmitToOde(List activeTim timToSend.setTim(tim); var extraEx = sendTim(timToSend, tum, activeTimId, reduced_mps); activeTimService.markForDeletion(activeTimId); - if (extraEx.size() > 0) { + if (!extraEx.isEmpty()) { exceptions.addAll(extraEx); } } catch (Exception ex) { exceptions.add(new ResubmitTimException(activeTimId, ex.getMessage())); } } - if (exceptions.size() > 0) { - utility.logWithDate("Errors occurred while resubmitting TIMs: " + gson.toJson(exceptions), - TimGenerationHelper.class); + if (!exceptions.isEmpty()) { + log.error("Errors occurred while resubmitting TIMs: {}", gson.toJson(exceptions)); } - return exceptions; } public boolean isValidTim(TimUpdateModel tum) { // start point var stPt = tum.getStartPoint(); - if (stPt == null || stPt.getLatitude() == null || stPt.getLongitude() == null) + if (stPt == null || stPt.getLatitude() == null || stPt.getLongitude() == null) { return false; + } // direction - if (tum.getDirection() == null || tum.getDirection().isEmpty()) + if (tum.getDirection() == null || tum.getDirection().isEmpty()) { return false; + } // route - if (tum.getRoute() == null || tum.getRoute().isEmpty()) + if (tum.getRoute() == null || tum.getRoute().isEmpty()) { return false; + } return true; } - private OdeTravelerInformationMessage getTim(TimUpdateModel aTim, List mps, List allMps, - Milepost anchor, boolean resetStartTimes) { + private OdeTravelerInformationMessage getTim(TimUpdateModel aTim, List mps, + List allMps, Milepost anchor, + boolean resetStartTimes) { return getTim(aTim, mps, allMps, anchor, resetStartTimes, false); } - private OdeTravelerInformationMessage getTim(TimUpdateModel aTim, List mps, List allMps, - Milepost anchor, boolean resetStartTimes, boolean resetExpirationTime) { + private OdeTravelerInformationMessage getTim(TimUpdateModel aTim, List mps, + List allMps, Milepost anchor, + boolean resetStartTimes, + boolean resetExpirationTime) { String nowAsISO = Instant.now().toString(); DataFrame dataFrame = getDataFrame(aTim, anchor, resetStartTimes, resetExpirationTime); // check to see if we have any itis codes // if not, just continue on if (dataFrame.getItems() == null || dataFrame.getItems().length == 0) { - utility.logWithDate("No itis codes found for data_frame " + aTim.getDataFrameId() + ". Skipping..."); + log.info( + "No itis codes found for data_frame " + aTim.getDataFrameId() + ". Skipping..."); return null; } - List regions = buildRegions(aTim, mps, allMps, anchor); - dataFrame.setRegions(regions.toArray(new OdeTravelerInformationMessage.DataFrame.Region[regions.size()])); + List regions = + buildRegions(aTim, mps, allMps, anchor); + dataFrame.setRegions( + regions.toArray(new OdeTravelerInformationMessage.DataFrame.Region[regions.size()])); DataFrame[] dataframes = new DataFrame[1]; dataframes[0] = dataFrame; OdeTravelerInformationMessage tim = new OdeTravelerInformationMessage(); tim.setDataframes(dataframes); - tim.setMsgCnt(getMsgCnt(aTim.getMsgCnt())); + tim.setMsgCnt(getNextMessageCount(aTim.getMsgCnt())); tim.setPacketID(aTim.getPacketId()); tim.setTimeStamp(nowAsISO); @@ -628,18 +734,18 @@ private OdeTravelerInformationMessage getTim(TimUpdateModel aTim, List * @return The list of regions built from the update model. */ private List buildRegions(TimUpdateModel aTim, - List reducedMileposts, List allMps, Milepost anchor) { + List reducedMileposts, + List allMps, + Milepost anchor) { if (reducedMileposts.size() <= 63) { - utility.logWithDate("Less than 63 mileposts, building a single region from update model.", - TimGenerationHelper.class); - List regions = new ArrayList(); - OdeTravelerInformationMessage.DataFrame.Region singleRegion = buildSingleRegionFromUpdateModel(aTim, - reducedMileposts, allMps, anchor); + log.info("Less than 63 mileposts, building a single region from update model."); + List regions = new ArrayList<>(); + OdeTravelerInformationMessage.DataFrame.Region singleRegion = + buildSingleRegionFromUpdateModel(aTim, reducedMileposts, allMps, anchor); regions.add(singleRegion); return regions; } else { - utility.logWithDate("More than 63 mileposts, building multiple regions from update model.", - TimGenerationHelper.class); + log.info("More than 63 mileposts, building multiple regions from update model."); return buildMultipleRegionsFromUpdateModel(aTim, reducedMileposts, allMps, anchor); } } @@ -654,8 +760,9 @@ private List buildRegions(TimUpd * @return The list of OdeTravelerInformationMessage.DataFrame.Region objects. */ private List buildMultipleRegionsFromUpdateModel( - TimUpdateModel aTim, List reducedMileposts, List allMps, Milepost anchor) { - List regions = new ArrayList(); + TimUpdateModel aTim, List reducedMileposts, List allMps, + Milepost anchor) { + List regions = new ArrayList<>(); int maxMilepostsPerRegion = 63; @@ -667,25 +774,26 @@ private List buildMultipleRegion milepostsForNextRegion.add(reducedMileposts.get(i)); // if we have reached the max number of mileposts per region, or if we are at // the end of the list - if (milepostsForNextRegion.size() == maxMilepostsPerRegion || i == reducedMileposts.size() - 1) { - OdeTravelerInformationMessage.DataFrame.Region region = buildSingleRegionFromUpdateModel(aTim, - milepostsForNextRegion, allMps, nextAnchor); + if (milepostsForNextRegion.size() == maxMilepostsPerRegion || + i == reducedMileposts.size() - 1) { + OdeTravelerInformationMessage.DataFrame.Region region = + buildSingleRegionFromUpdateModel(aTim, milepostsForNextRegion, allMps, + nextAnchor); regions.add(region); milepostsForNextRegion.clear(); nextAnchor = reducedMileposts.get(i); } } - - utility.logWithDate("Built " + regions.size() + " regions from update model.", TimGenerationHelper.class); + log.info("Built {} regions from update model.", regions.size()); return regions; } - private List sendTim(WydotTravelerInputData timToSend, TimUpdateModel tum, Long activeTimId, - List reduced_mps) { + private List sendTim(WydotTravelerInputData timToSend, TimUpdateModel tum, + Long activeTimId, List reduced_mps) { List exceptions = new ArrayList<>(); // try to send to RSU if not a sat TIM and along route with RSUs - if (StringUtils.isBlank(tum.getSatRecordId()) - && Arrays.asList(config.getRsuRoutes()).contains(tum.getRoute())) { + if (StringUtils.isBlank(tum.getSatRecordId()) && + Arrays.asList(config.getRsuRoutes()).contains(tum.getRoute())) { var exMsg = updateAndSendRSU(timToSend, tum); if (StringUtils.isNotBlank(exMsg)) { exceptions.add(new ResubmitTimException(activeTimId, exMsg)); @@ -699,27 +807,25 @@ private List sendTim(WydotTravelerInputData timToSend, Tim exceptions.add(new ResubmitTimException(activeTimId, exMsg)); } } else { - String exMsg = "active_tim_id " + tum.getActiveTimId() - + " not sent to SDX (no SAT_RECORD_ID found in database)"; - utility.logWithDate(exMsg); + String exMsg = "active_tim_id " + tum.getActiveTimId() + + " not sent to SDX (no SAT_RECORD_ID found in database)"; + log.error(exMsg); } - if (exceptions.size() > 0) { - utility.logWithDate("Errors occurred while resubmitting TIMs: " + gson.toJson(exceptions), - TimGenerationHelper.class); + if (!exceptions.isEmpty()) { + log.error("Errors occurred while resubmitting TIMs: {}", gson.toJson(exceptions)); } return exceptions; } - private int getMsgCnt(int currentCnt) { - if (currentCnt == 127) - return 1; - // else increment msgCnt - else - return currentCnt++; + protected int getNextMessageCount(int currentCount) { + if (currentCount == 127) { + currentCount = 0; + } + return ++currentCount; } private DataFrame getDataFrame(TimUpdateModel aTim, Milepost anchor, boolean resetStartTimes, - boolean resetExpirationTime) { + boolean resetExpirationTime) { // RoadSignID RoadSignID rsid = new RoadSignID(); rsid.setPosition(getAnchorPosition(anchor)); @@ -727,7 +833,8 @@ private DataFrame getDataFrame(TimUpdateModel aTim, Milepost anchor, boolean res // if we are coming in with content=speedLimit and frameType=roadSignage, // we need to set the mutcdCode to regulatory to display the regulatory signage - if (aTim.getDfContent() == ContentEnum.speedLimit && aTim.getFrameType() == TravelerInfoType.roadSignage) { + if (aTim.getDfContent() == ContentEnum.speedLimit && + aTim.getFrameType() == TravelerInfoType.roadSignage) { rsid.setMutcdCode(MutcdCodeEnum.regulatory); } else { rsid.setMutcdCode(MutcdCodeEnum.warning); @@ -739,15 +846,12 @@ private DataFrame getDataFrame(TimUpdateModel aTim, Milepost anchor, boolean res // DataFrame DataFrame df = new DataFrame(); - df.setNotUsed(aTim.getNotUsed()); df.setFrameType(aTim.getFrameType()); df.setMsgId(msgId); df.setPriority(5);// 0-7, 0 being least important, 7 being most - df.setNotUsed1(aTim.getNotUsed1()); - df.setNotUsed3(aTim.getNotUsed3()); - df.setNotUsed2(aTim.getNotUsed2()); - if (aTim.getDfContent() != null) + if (aTim.getDfContent() != null) { df.setContent(aTim.getDfContent().getStringValue()); + } df.setUrl(aTim.getUrl()); // set startTime @@ -764,7 +868,8 @@ private DataFrame getDataFrame(TimUpdateModel aTim, Milepost anchor, boolean res if (resetExpirationTime) { df.setDurationTime(5); // set to non-zero so RSUs send to OBUs } else if (aTim.getEndDateTime() != null) { - int durationTime = utility.getMinutesDurationBetweenTwoDates(df.getStartDateTime(), aTim.getEndDateTime()); + int durationTime = utility.getMinutesDurationBetweenTwoDates(df.getStartDateTime(), + aTim.getEndDateTime()); // J2735 has duration time of 0-32000 // the ODE fails if we have greater than 32000 if (durationTime > 32000) { @@ -789,7 +894,7 @@ private DataFrame getDataFrame(TimUpdateModel aTim, Milepost anchor, boolean res * longitude of the anchor. * If both the TimUpdateModel and the anchor are null, the anchor position is * set to latitude 0, longitude 0, and elevation 0. - * + * * @param anchor The Milepost representing the anchor. * @return The OdePosition3D object representing the anchor position. */ @@ -818,8 +923,9 @@ private OdePosition3D getAnchorPosition(Milepost anchor) { * @param anchor The anchor milepost. * @return The built Region object. */ - private Region buildSingleRegionFromUpdateModel(TimUpdateModel aTim, List reducedMileposts, - List allMps, Milepost anchor) { + private Region buildSingleRegionFromUpdateModel(TimUpdateModel aTim, + List reducedMileposts, + List allMps, Milepost anchor) { Region region = new Region(); // TODO: set name? // TODO: set regulator id? @@ -841,10 +947,12 @@ private Region buildSingleRegionFromUpdateModel(TimUpdateModel aTim, List wydotRsus = rsuService.getFullRsusTimIsOn(aTim.getTimId()); List dbRsus = new ArrayList(); if (wydotRsus == null || wydotRsus.size() <= 0) { - utility.logWithDate("RSUs not found to update db for active_tim_id " + aTim.getActiveTimId()); + log.info("No RSUs found for active_tim_id {}", aTim.getActiveTimId()); - dbRsus = rsuService.getRsusByLatLong(aTim.getDirection(), aTim.getStartPoint(), aTim.getEndPoint(), - aTim.getRoute()); + dbRsus = rsuService.getRsusByLatLong(aTim.getDirection(), aTim.getStartPoint(), + aTim.getEndPoint(), aTim.getRoute()); // if no RSUs found - if (dbRsus.size() == 0) { + if (dbRsus.isEmpty()) { exMsg = "No possible RSUs found for active_tim_id " + aTim.getActiveTimId(); - utility.logWithDate(exMsg); + log.error(exMsg); return exMsg; } } // set SNMP command - String startTimeString = aTim.getStartDate_Timestamp() != null - ? aTim.getStartDate_Timestamp().toInstant().toString() - : ""; - String endTimeString = aTim.getEndDate_Timestamp() != null ? aTim.getEndDate_Timestamp().toInstant().toString() - : ""; + String startTimeString = aTim.getStartDate_Timestamp() != null ? + aTim.getStartDate_Timestamp().toInstant().toString() : ""; + String endTimeString = aTim.getEndDate_Timestamp() != null ? + aTim.getEndDate_Timestamp().toInstant().toString() : ""; SNMP snmp = snmpHelper.getSnmp(startTimeString, endTimeString, timToSend); timToSend.setRequest(new ServiceRequest()); timToSend.getRequest().setSnmp(snmp); RSU[] rsus = new RSU[1]; - if (wydotRsus.size() > 0) { + if (wydotRsus != null && !wydotRsus.isEmpty()) { RSU rsu = null; for (int i = 0; i < wydotRsus.size(); i++) { var wydotRsu = wydotRsus.get(i); @@ -948,12 +1056,13 @@ private String updateAndSendRSU(WydotTravelerInputData timToSend, TimUpdateModel try { regionName = regionNameTrimmer.trimRegionNameIfTooLong(regionName); } catch (IllegalArgumentException e) { - utility.logWithDate("Failed to trim region name: " + e.getMessage()); + log.error("Failed to trim region name: {}", e.getMessage()); } timToSend.getTim().getDataframes()[0].getRegions()[0].setName(regionName); - utility.logWithDate("Sending TIM to RSU for refresh: " + gson.toJson(timToSend)); + log.info("Sending TIM to RSU for refresh: {}", gson.toJson(timToSend)); - var rsuClearExMsg = odeService.deleteTimFromRsu(rsu, Integer.valueOf(wydotRsu.getIndex())); + var rsuClearExMsg = + odeService.deleteTimFromRsu(rsu, Integer.valueOf(wydotRsu.getIndex())); var rsuExMsg = odeService.sendNewTimToRsu(timToSend); if (!StringUtils.isEmpty(rsuClearExMsg)) { @@ -967,65 +1076,60 @@ private String updateAndSendRSU(WydotTravelerInputData timToSend, TimUpdateModel } else { // we don't have any existing RSUs, but some fall within the boundary so send // new ones there. We need to update requestName in this case - for (int i = 0; i < dbRsus.size(); i++) { - String regionName = getRsuRegionName(aTim, dbRsus.get(i)); + for (WydotRsu rsu : dbRsus) { + String regionName = getRsuRegionName(aTim, rsu); try { regionName = regionNameTrimmer.trimRegionNameIfTooLong(regionName); } catch (IllegalArgumentException e) { - utility.logWithDate("Failed to trim region name: " + e.getMessage()); + log.error("Failed to trim region name: {}", e.getMessage()); } timToSend.getTim().getDataframes()[0].getRegions()[0].setName(regionName); - rsus[0] = dbRsus.get(i); + rsus[0] = rsu; timToSend.getRequest().setRsus(rsus); // get next index - TimQuery timQuery = odeService.submitTimQuery(dbRsus.get(i), 0); + TimQuery timQuery = odeService.submitTimQuery(rsu, 0); // query failed, don't send TIM // log the error and continue if (timQuery == null) { WydotRsu wydotRsu = (WydotRsu) timToSend.getRequest().getRsus()[0]; - var tmpErrMsg = "Returning without sending TIM to RSU. submitTimQuery failed for RSU " - + gson.toJson(wydotRsu); + var tmpErrMsg = + "Returning without sending TIM to RSU. submitTimQuery failed for RSU " + + gson.toJson(wydotRsu); exMsg += tmpErrMsg + "\n"; - utility.logWithDate(tmpErrMsg); + log.error(tmpErrMsg); continue; } // Fetch existing active_tim_holding records. If other TIMs are en route to this // RSU, make sure we don't overwrite their claimed indexes - List existingHoldingRecords = activeTimHoldingService - .getActiveTimHoldingForRsu(dbRsus.get(i).getRsuTarget()); + List existingHoldingRecords = + activeTimHoldingService.getActiveTimHoldingForRsu(rsu.getRsuTarget()); existingHoldingRecords.forEach(x -> timQuery.appendIndex(x.getRsuIndex())); // Finally, fetch all active_tims that are supposed to be on this RSU. Some may // not be there, due to network or RSU issues. Make sure we don't claim an index // that's already been claimed. - List claimedIndexes = rsuService.getActiveRsuTimIndexes(dbRsus.get(i).getRsuId()); + List claimedIndexes = rsuService.getActiveRsuTimIndexes(rsu.getRsuId()); claimedIndexes.forEach(x -> timQuery.appendIndex(x)); - Integer nextRsuIndex = odeService.findFirstAvailableIndexWithRsuIndex(timQuery.getIndicies_set()); + Integer nextRsuIndex = + odeService.findFirstAvailableIndexWithRsuIndex(timQuery.getIndicies_set()); // unable to find next available index // log error and continue if (nextRsuIndex == null) { WydotRsu wydotRsu = (WydotRsu) timToSend.getRequest().getRsus()[0]; - var tmpErrMsg = "Unable to find an available index for RSU " + gson.toJson(wydotRsu); + var tmpErrMsg = + "Unable to find an available index for RSU " + gson.toJson(wydotRsu); exMsg += tmpErrMsg + "\n"; - utility.logWithDate(tmpErrMsg); + log.error(tmpErrMsg); continue; } // create new active_tim_holding record, to account for any index changes - WydotTim wydotTim = new WydotTim(); - wydotTim.setClientId(aTim.getClientId()); - wydotTim.setDirection(aTim.getDirection()); - wydotTim.setStartPoint(aTim.getStartPoint()); - wydotTim.setEndPoint(aTim.getEndPoint()); - ActiveTimHolding activeTimHolding = new ActiveTimHolding(wydotTim, dbRsus.get(i).getRsuTarget(), null, - aTim.getEndPoint()); - activeTimHolding.setRsuIndex(nextRsuIndex); - activeTimHolding.setPacketId(timToSend.getTim().getPacketID()); - activeTimHoldingService.insertActiveTimHolding(activeTimHolding); + createNewActiveTimHoldingRecord(timToSend.getTim().getPacketID(), aTim, + rsu.getRsuTarget(), nextRsuIndex, null); // set msgCnt to 1 and create new packetId timToSend.getTim().setMsgCnt(1); @@ -1035,14 +1139,15 @@ private String updateAndSendRSU(WydotTravelerInputData timToSend, TimUpdateModel if (!StringUtils.isEmpty(newRsuEx)) { exMsg += newRsuEx + "\n"; } - rsus[0] = dbRsus.get(i); + rsus[0] = rsu; timToSend.getRequest().setRsus(rsus); } } return exMsg; } - private String updateAndSendSDX(WydotTravelerInputData timToSend, TimUpdateModel aTim, List reduced_mps) { + private String updateAndSendSDX(WydotTravelerInputData timToSend, TimUpdateModel aTim, + List reduced_mps) { // Ensure request is empty timToSend.setRequest(new ServiceRequest()); @@ -1058,14 +1163,18 @@ private String updateAndSendSDX(WydotTravelerInputData timToSend, TimUpdateModel try { regionName = regionNameTrimmer.trimRegionNameIfTooLong(regionName); } catch (IllegalArgumentException e) { - utility.logWithDate("Failed to trim region name: " + e.getMessage()); + log.error("Failed to trim region name: {}", e.getMessage()); } timToSend.getTim().getDataframes()[0].getRegions()[0].setName(regionName); // set sdw block in TIM timToSend.getRequest().setSdw(sdw); - utility.logWithDate("Sending TIM to SDW for refresh: " + gson.toJson(timToSend)); + // create new active_tim_holding record + createNewActiveTimHoldingRecord(timToSend.getTim().getPacketID(), aTim, null, null, + aTim.getSatRecordId()); + + log.info("Sending TIM to SDW for refresh: {}", gson.toJson(timToSend)); return odeService.updateTimOnSdw(timToSend); } @@ -1075,7 +1184,7 @@ private OdeGeoRegion getServiceRegion(List mileposts) { Comparator compLong = (l1, l2) -> l1.getLongitude().compareTo(l2.getLongitude()); OdeGeoRegion serviceRegion = new OdeGeoRegion(); - if (mileposts.size() > 0) { + if (!mileposts.isEmpty()) { Milepost maxLat = mileposts.stream().max(compLat).get(); @@ -1102,20 +1211,29 @@ private OdeGeoRegion getServiceRegion(List mileposts) { } /** - * This method returns the anchor point for the given mileposts. - * - * @param firstPoint The first milepost. - * @param secondPoint The second milepost. - * @return The anchor point as a Milepost. + * Creates a new ActiveTimHolding record and inserts it into the database. + * + * @param packetId The packet ID associated with the TIM. + * @param aTim The TimUpdateModel containing the TIM update information. + * @param rsuTarget The target RSU (Road Side Unit) for the TIM. + * @param nextRsuIndex The next available RSU index. + * @param satRecordId The satellite record ID, if applicable. */ - private Milepost getAnchorPoint(Milepost firstPoint, Milepost secondPoint) { - Coordinate anchorCoordinate = utility.calculateAnchorCoordinate(firstPoint, secondPoint); - - Milepost anchor = new Milepost(); - anchor.setLatitude(anchorCoordinate.getLatitude()); - anchor.setLongitude(anchorCoordinate.getLongitude()); - anchor.setMilepost(firstPoint.getMilepost()); - anchor.setDirection(firstPoint.getDirection()); - return anchor; + private void createNewActiveTimHoldingRecord(String packetId, TimUpdateModel aTim, + String rsuTarget, Integer nextRsuIndex, + String satRecordId) { + // Create a new WydotTim object and set its properties from the TimUpdateModel + WydotTim wydotTim = new WydotTim(aTim); + + // Create a new ActiveTimHolding object with the WydotTim, RSU target, satellite record ID, and end point + ActiveTimHolding activeTimHolding = + new ActiveTimHolding(wydotTim, rsuTarget, satRecordId, aTim.getEndPoint()); + + // Set the RSU index and packet ID for the ActiveTimHolding + activeTimHolding.setRsuIndex(nextRsuIndex); + activeTimHolding.setPacketId(packetId); + + // Insert the ActiveTimHolding record into the database + activeTimHoldingService.insertActiveTimHolding(activeTimHolding); } } \ No newline at end of file diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/Utility.java b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/Utility.java index 2ceda94e0..4c20350df 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/helpers/Utility.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/helpers/Utility.java @@ -1,12 +1,8 @@ package com.trihydro.library.helpers; -import java.io.IOException; import static java.lang.Math.toIntExact; + import java.math.BigDecimal; -import java.net.HttpURLConnection; -import java.net.URL; -import java.sql.ResultSet; -import java.sql.SQLException; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -23,325 +19,324 @@ @Component public class Utility { - private DateFormat utcFormatMilliSec = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - private DateFormat utcFormatSec = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); - private DateFormat utcFormatMin = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); - public DateFormat timestampFormat = new SimpleDateFormat("dd-MMM-yy hh.mm.ss.SSS a"); - public DateFormat utcTextFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z[UTC]'"); - - public Gson gson = new Gson(); - - public Date convertDate(String incomingDate) { - Date convertedDate = null; - try { - if (incomingDate != null) { - if (incomingDate.contains("UTC")) { - utcTextFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - convertedDate = utcTextFormat.parse(incomingDate); - } - else if (incomingDate.contains(".")) { - utcFormatMilliSec.setTimeZone(TimeZone.getTimeZone("UTC")); - convertedDate = utcFormatMilliSec.parse(incomingDate); - } - else if (incomingDate.length() == 17) { - utcFormatMin.setTimeZone(TimeZone.getTimeZone("UTC")); - convertedDate = utcFormatMin.parse(incomingDate); - } - else { - utcFormatSec.setTimeZone(TimeZone.getTimeZone("UTC")); - convertedDate = utcFormatSec.parse(incomingDate); - } - } - } catch (ParseException e1) { - e1.printStackTrace(); - } - return convertedDate; - } - - public void logWithDate(String msg, Class clazz) { - logWithDate(clazz.getSimpleName() + ": " + msg); - } - - public void logWithDate(String msg) { - Date date = new Date(); - System.out.println(date + " " + msg); - } - - public int getMinutesDurationBetweenTwoDates(String startDateTime, String endDateTime) { - int duration = getMinutesDurationWithSimpleDateFormat(startDateTime, endDateTime); - if (duration == -1) { - duration = getMinutesDurationWithZonedDateTime(startDateTime, endDateTime); - } - if (duration == -1) { - duration = getMinutesDurationWithYyMmDdFormat(startDateTime, endDateTime); - } - if (duration == -1) { - // dates may be in different formats, attempt to identify formats & translate to ZonedDateTime, then calculate - String startDateTimeInZonedDateTime; - try { - startDateTimeInZonedDateTime = translateToZonedDateTime(startDateTime); - } catch (UnrecognizedDateFormatException e) { - logWithDate("Failed to parse dates when getting minutes between: " + startDateTime + " and " + endDateTime + ". Unrecognized date format: " + startDateTime); - return -1; - } - - String endDateTimeInZonedDateTime; - try { - endDateTimeInZonedDateTime = translateToZonedDateTime(endDateTime); - } catch (UnrecognizedDateFormatException e) { - logWithDate("Failed to parse dates when getting minutes between: " + startDateTime + " and " + endDateTime + ". Unrecognized date format: " + startDateTime); - return -1; - } - - duration = getMinutesDurationWithZonedDateTime(startDateTimeInZonedDateTime, endDateTimeInZonedDateTime); - } - if (duration == -1) { - logWithDate("Failed to parse dates when getting minutes between: " + startDateTime + " and " + endDateTime); - } - return duration; - } - - /** - * Attempt to get duration in minutes between two dates parsed as ZonedDateTime. - * If this fails, return -1 - * - * @param startDateTime - * @param endDateTime - * @return The duration in minutes between the two given dates. If parsing - * fails, returns -1 - */ - private int getMinutesDurationWithZonedDateTime(String startDateTime, String endDateTime) { - try { - ZonedDateTime zdtStart = ZonedDateTime.parse(startDateTime); - ZonedDateTime zdtEnd = ZonedDateTime.parse(endDateTime); - - java.time.Duration dateDuration = java.time.Duration.between(zdtStart, zdtEnd); - long durationTime = Math.abs(dateDuration.toMinutes()); - - return toIntExact(durationTime); - } catch (DateTimeParseException exception) { - return -1; - } - } - - /** - * Attempt to get duration in minutes between two dates parsed in - * SimpleDateFormat("dd-MMM-yy HH.mm.ss"). If parsing fails, returns -1 - * - * @param startDateTime - * @param endDateTime - * @return The duration in minutes between the two given dates. If parsing - * fails, returns -1 - */ - private int getMinutesDurationWithSimpleDateFormat(String startDateTime, String endDateTime) { - try { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MMM-yy HH.mm.ss"); - Date startDate = simpleDateFormat.parse(startDateTime); - Date endDate = simpleDateFormat.parse(endDateTime); - - long duration = (endDate.getTime() - startDate.getTime()) / 60000; // milliseconds to minutes is 1/60000 - return toIntExact(duration); - } catch (Exception ex) { - return -1; - } - } - - /** - * Attempt to get duration in minutes between two dates parsed in - * SimpleDateFormat("yyyy-MM-dd HH:mm:ss"). If parsing fails, returns -1 - * - * @param startDateTime - * @param endDateTime - * @return The duration in minutes between the two given dates. If parsing - * fails, returns -1 - */ - private int getMinutesDurationWithYyMmDdFormat(String startDateTime, String endDateTime) { - try { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - Date startDate = simpleDateFormat.parse(startDateTime); - Date endDate = simpleDateFormat.parse(endDateTime); - - long duration = (endDate.getTime() - startDate.getTime()) / 60000; // milliseconds to minutes is 1/60000 - return toIntExact(duration); - } catch (Exception ex) { - return -1; - } - } - - /** - * Checks the format of the date string and translates it to a ZonedDateTime if possible. - * Throws an UnrecognizedDateFormatException if the date format is not recognized. - * @param dateTimeString The date string to translate - * @return The date string translated to a ZonedDateTime - * @throws UnrecognizedDateFormatException If the date format is not recognized - */ - private String translateToZonedDateTime(String dateTimeString) throws UnrecognizedDateFormatException { - // if already ZonedDateTime, return - try { - ZonedDateTime.parse(dateTimeString); - return dateTimeString; - } catch(DateTimeParseException exception) {} - - // if not ZonedDateTime, check for simple date format - try { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MMM-yy HH.mm.ss"); - Date startDate = simpleDateFormat.parse(dateTimeString); - - // translate to ZonedDateTime - return ZonedDateTime.ofInstant(startDate.toInstant(), java.time.ZoneId.systemDefault()).toString(); - } catch(Exception e) {} - - // if not ZonedDateTime or SimpleDateFormat, check for YyMmDdFormat - try { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - Date startDate = simpleDateFormat.parse(dateTimeString); - - // translate to ZonedDateTime - return ZonedDateTime.ofInstant(startDate.toInstant(), java.time.ZoneId.systemDefault()).toString(); - } catch (Exception e) {} - - throw new UnrecognizedDateFormatException("Unrecognized date format: " + dateTimeString); - } - - /** - * Returns the value presented by the ResultSet at key if available. If the - * value presented is null, defaults to 1 (rather than short default of 0). Used - * for our purposes to default to 1 for various values. - * - * @param rs The ResultSet to pull data from - * @param key The key value to use to get data from the ResultSet - * @return Short value defaulted to 1 if not found - */ - public short GetShortValueFromResultSet(ResultSet rs, String key) { - try { - String value = rs.getString(key); - if (value != null) { - return Short.valueOf(value); - } - } catch (SQLException ex) { - System.out.println("Error attempting to get short value '" + key + "' from ResultSet"); - } - return (short) 1; - } - - public int getDirection(Double bearing) { - - int direction = 0; - - if (bearing >= 0 && bearing <= 22.5) - direction = 1; - else if (bearing > 22.5 && bearing <= 45) - direction = 2; - else if (bearing > 45 && bearing <= 67.5) - direction = 4; - else if (bearing > 67.5 && bearing <= 90) - direction = 8; - else if (bearing > 90 && bearing <= 112.5) - direction = 16; - else if (bearing > 112.5 && bearing <= 135) - direction = 32; - else if (bearing > 135 && bearing <= 157.5) - direction = 64; - else if (bearing > 157.5 && bearing <= 180) - direction = 128; - else if (bearing > 180 && bearing <= 202.5) - direction = 256; - else if (bearing > 202.5 && bearing <= 225) - direction = 512; - else if (bearing > 225 && bearing <= 247.5) - direction = 1024; - else if (bearing > 247.5 && bearing <= 270) - direction = 2048; - else if (bearing > 270 && bearing <= 292.5) - direction = 4096; - else if (bearing > 292.5 && bearing <= 315) - direction = 8192; - else if (bearing > 315 && bearing <= 337.5) - direction = 16384; - else if (bearing > 337.5 && bearing <= 360) - direction = 32768; - - return direction; - } - - /** - * Creates a connection with authentication via an apikey and returning JSON. - * Used to send HTTP requests to the SDX api - * - * @param method The HTTP method to use (GET,POST,PUT,DELETE) - * @param url The URL to send the request to - * @param apiKey The apikey value to apply in the header - * @return - * @throws IOException - */ - public HttpURLConnection getSdxUrlConnection(String method, URL url, String apiKey) throws IOException { - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setRequestMethod(method); - conn.setRequestProperty("Accept", "application/json"); - conn.setRequestProperty("apikey", apiKey); - - return conn; - } + private final DateFormat utcFormatMilliSec = + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + private final DateFormat utcFormatSec = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + private final DateFormat utcFormatMin = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); + public DateFormat timestampFormat = new SimpleDateFormat("dd-MMM-yy hh.mm.ss.SSS a"); + public DateFormat utcTextFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z[UTC]'"); + + public Gson gson = new Gson(); + + public Date convertDate(String incomingDate) { + Date convertedDate = null; + try { + if (incomingDate != null) { + if (incomingDate.contains("UTC")) { + utcTextFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + convertedDate = utcTextFormat.parse(incomingDate); + } else if (incomingDate.contains(".")) { + utcFormatMilliSec.setTimeZone(TimeZone.getTimeZone("UTC")); + convertedDate = utcFormatMilliSec.parse(incomingDate); + } else if (incomingDate.length() == 17) { + utcFormatMin.setTimeZone(TimeZone.getTimeZone("UTC")); + convertedDate = utcFormatMin.parse(incomingDate); + } else { + utcFormatSec.setTimeZone(TimeZone.getTimeZone("UTC")); + convertedDate = utcFormatSec.parse(incomingDate); + } + } + } catch (ParseException e1) { + e1.printStackTrace(); + } + return convertedDate; + } + + public void logWithDate(String msg, Class clazz) { + logWithDate(clazz.getSimpleName() + ": " + msg); + } + + public void logWithDate(String msg) { + Date date = new Date(); + System.out.println(date + " " + msg); + } + + public int getMinutesDurationBetweenTwoDates(String startDateTime, String endDateTime) { + int duration = getMinutesDurationWithSimpleDateFormat(startDateTime, endDateTime); + if (duration == -1) { + duration = getMinutesDurationWithZonedDateTime(startDateTime, endDateTime); + } + if (duration == -1) { + duration = getMinutesDurationWithYyMmDdFormat(startDateTime, endDateTime); + } + if (duration == -1) { + // dates may be in different formats, attempt to identify formats & translate to ZonedDateTime, then calculate + String startDateTimeInZonedDateTime; + try { + startDateTimeInZonedDateTime = translateToZonedDateTime(startDateTime); + } catch (UnrecognizedDateFormatException e) { + logWithDate("Failed to parse dates when getting minutes between: " + startDateTime + + " and " + endDateTime + ". Unrecognized date format: " + startDateTime); + return -1; + } + + String endDateTimeInZonedDateTime; + try { + endDateTimeInZonedDateTime = translateToZonedDateTime(endDateTime); + } catch (UnrecognizedDateFormatException e) { + logWithDate("Failed to parse dates when getting minutes between: " + startDateTime + + " and " + endDateTime + ". Unrecognized date format: " + startDateTime); + return -1; + } + + duration = getMinutesDurationWithZonedDateTime(startDateTimeInZonedDateTime, + endDateTimeInZonedDateTime); + } + if (duration == -1) { + logWithDate( + "Failed to parse dates when getting minutes between: " + startDateTime + " and " + + endDateTime); + } + return duration; + } /** - * Calculates an anchor point for a TIM path. The anchor point is determined by - * moving 15 meters upstream from the first point of the original TIM path. The first - * two points of the TIM path are used to determine a bearing direction of this initial - * section of the path. The anchor point is calculated as 15 meters from the first path - * point, in the opposite direction of the initial path bearing. The earth surface - * distance calculations are implemented very roughly (optimized for fast calculation) - * because there is no need for high accuracy for the anchor point position. - * Two shortcuts were applied for the earth surface distance calculations: - * 1) a spherical earth is assumed with a fixed 111195 meters per surface degree - * distance to calculate the delta degrees difference between the first two path points - * 2) a flat plane assumption was used to calculate the bearing line distance from - * the previously calculated latitude and longitude delta distance (d0Latitude, d0Longitude), - * and these delta distances provide the flat plane bearing direction to move upstream to - * reach the anchor point - * - * @param firstPoint The first milepost. - * @param secondPoint The second milepost. - * @return The anchor coordinate. + * Attempt to get duration in minutes between two dates parsed as ZonedDateTime. + * If this fails, return -1 + * + * @param startDateTime The start date and time + * @param endDateTime The end date and time + * @return The duration in minutes between the two given dates. If parsing + * fails, returns -1 */ - public Coordinate calculateAnchorCoordinate(Milepost firstPoint, Milepost secondPoint) { - int precision = 9; - int metersPerSurfaceDegree = 111195; + private int getMinutesDurationWithZonedDateTime(String startDateTime, String endDateTime) { + try { + ZonedDateTime zdtStart = ZonedDateTime.parse(startDateTime); + ZonedDateTime zdtEnd = ZonedDateTime.parse(endDateTime); + + java.time.Duration dateDuration = java.time.Duration.between(zdtStart, zdtEnd); + long durationTime = Math.abs(dateDuration.toMinutes()); + + return toIntExact(durationTime); + } catch (DateTimeParseException exception) { + return -1; + } + } - BigDecimal firstPointLatitude = firstPoint.getLatitude().round(new java.math.MathContext(precision)); - BigDecimal firstPointLongitude = firstPoint.getLongitude().round(new java.math.MathContext(precision)); - BigDecimal secondPointLatitude = secondPoint.getLatitude().round(new java.math.MathContext(precision)); - BigDecimal secondPointLongitude = secondPoint.getLongitude().round(new java.math.MathContext(precision)); + /** + * Attempt to get duration in minutes between two dates parsed in + * SimpleDateFormat("dd-MMM-yy HH.mm.ss"). If parsing fails, returns -1 + * + * @param startDateTime The start date and time + * @param endDateTime The end date and time + * @return The duration in minutes between the two given dates. If parsing + * fails, returns -1 + */ + private int getMinutesDurationWithSimpleDateFormat(String startDateTime, String endDateTime) { + try { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MMM-yy HH.mm.ss"); + Date startDate = simpleDateFormat.parse(startDateTime); + Date endDate = simpleDateFormat.parse(endDateTime); + + long duration = (endDate.getTime() - startDate.getTime()) / + 60000; // milliseconds to minutes is 1/60000 + return toIntExact(duration); + } catch (Exception ex) { + return -1; + } + } - // 1) Get the difference in latitude between the first and second points. + /** + * Attempt to get duration in minutes between two dates parsed in + * SimpleDateFormat("yyyy-MM-dd HH:mm:ss"). If parsing fails, returns -1 + * + * @param startDateTime The start date and time + * @param endDateTime The end date and time + * @return The duration in minutes between the two given dates. If parsing + * fails, returns -1 + */ + private int getMinutesDurationWithYyMmDdFormat(String startDateTime, String endDateTime) { + try { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date startDate = simpleDateFormat.parse(startDateTime); + Date endDate = simpleDateFormat.parse(endDateTime); + + long duration = (endDate.getTime() - startDate.getTime()) / + 60000; // milliseconds to minutes is 1/60000 + return toIntExact(duration); + } catch (Exception ex) { + return -1; + } + } + + /** + * Checks the format of the date string and translates it to a ZonedDateTime if possible. + * Throws an UnrecognizedDateFormatException if the date format is not recognized. + * + * @param dateTimeString The date string to translate + * @return The date string translated to a ZonedDateTime + * @throws UnrecognizedDateFormatException If the date format is not recognized + */ + private String translateToZonedDateTime(String dateTimeString) + throws UnrecognizedDateFormatException { + // if already ZonedDateTime, return + try { + ZonedDateTime.parse(dateTimeString); + return dateTimeString; + } catch (DateTimeParseException ignored) { + } + + // if not ZonedDateTime, check for simple date format + try { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MMM-yy HH.mm.ss"); + Date startDate = simpleDateFormat.parse(dateTimeString); + + // translate to ZonedDateTime + return ZonedDateTime.ofInstant(startDate.toInstant(), java.time.ZoneId.systemDefault()) + .toString(); + } catch (Exception ignored) { + } + + // if not ZonedDateTime or SimpleDateFormat, check for YyMmDdFormat + try { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date startDate = simpleDateFormat.parse(dateTimeString); + + // translate to ZonedDateTime + return ZonedDateTime.ofInstant(startDate.toInstant(), java.time.ZoneId.systemDefault()) + .toString(); + } catch (Exception ignored) { + } + + throw new UnrecognizedDateFormatException("Unrecognized date format: " + dateTimeString); + } + + public int getDirection(Double bearing) { + + int direction = 0; + + if (bearing >= 0 && bearing <= 22.5) { + direction = 1; + } else if (bearing > 22.5 && bearing <= 45) { + direction = 2; + } else if (bearing > 45 && bearing <= 67.5) { + direction = 4; + } else if (bearing > 67.5 && bearing <= 90) { + direction = 8; + } else if (bearing > 90 && bearing <= 112.5) { + direction = 16; + } else if (bearing > 112.5 && bearing <= 135) { + direction = 32; + } else if (bearing > 135 && bearing <= 157.5) { + direction = 64; + } else if (bearing > 157.5 && bearing <= 180) { + direction = 128; + } else if (bearing > 180 && bearing <= 202.5) { + direction = 256; + } else if (bearing > 202.5 && bearing <= 225) { + direction = 512; + } else if (bearing > 225 && bearing <= 247.5) { + direction = 1024; + } else if (bearing > 247.5 && bearing <= 270) { + direction = 2048; + } else if (bearing > 270 && bearing <= 292.5) { + direction = 4096; + } else if (bearing > 292.5 && bearing <= 315) { + direction = 8192; + } else if (bearing > 315 && bearing <= 337.5) { + direction = 16384; + } else if (bearing > 337.5 && bearing <= 360) { + direction = 32768; + } + + return direction; + } + + /** + * Calculates an anchor point for a TIM path. The anchor point is determined by + * moving 15 meters upstream from the first point of the original TIM path. The first + * two points of the TIM path are used to determine a bearing direction of this initial + * section of the path. The anchor point is calculated as 15 meters from the first path + * point, in the opposite direction of the initial path bearing. The earth surface + * distance calculations are implemented very roughly (optimized for fast calculation) + * because there is no need for high accuracy for the anchor point position. + * Two shortcuts were applied for the earth surface distance calculations: + * 1) a spherical earth is assumed with a fixed 111195 meters per surface degree + * distance to calculate the delta degrees difference between the first two path points + * 2) a flat plane assumption was used to calculate the bearing line distance from + * the previously calculated latitude and longitude delta distance (d0Latitude, d0Longitude), + * and these delta distances provide the flat plane bearing direction to move upstream to + * reach the anchor point + * + * @param firstPoint The first milepost. + * @param secondPoint The second milepost. + * @return The anchor coordinate. + * @throws IdenticalPointsException If the first and second points are identical, as this would result in a division by zero. + */ + public Coordinate calculateAnchorCoordinate(Milepost firstPoint, Milepost secondPoint) + throws IdenticalPointsException { + int precision = 9; + int metersPerSurfaceDegree = 111195; + + BigDecimal firstPointLatitude = + firstPoint.getLatitude().round(new java.math.MathContext(precision)); + BigDecimal firstPointLongitude = + firstPoint.getLongitude().round(new java.math.MathContext(precision)); + BigDecimal secondPointLatitude = + secondPoint.getLatitude().round(new java.math.MathContext(precision)); + BigDecimal secondPointLongitude = + secondPoint.getLongitude().round(new java.math.MathContext(precision)); + + // Check if the first and second points are identical + if (firstPointLatitude.equals(secondPointLatitude) && + firstPointLongitude.equals(secondPointLongitude)) { + throw new IdenticalPointsException(); + } + + // 1) Get the difference in latitude between the first and second points. BigDecimal differenceInLatitude = firstPointLatitude.subtract(secondPointLatitude); // 2) Get the difference in longitude between the first and second points. - BigDecimal differenceInLongitude = firstPointLongitude.subtract(secondPointLongitude); + BigDecimal differenceInLongitude = firstPointLongitude.subtract(secondPointLongitude); - // 3) Multiply the difference in latitude by the meters per surface degree to get d0Latitude. - BigDecimal d0Latitude = new BigDecimal(metersPerSurfaceDegree).multiply(differenceInLatitude); + // 3) Multiply the difference in latitude by the meters per surface degree to get d0Latitude. + BigDecimal d0Latitude = + new BigDecimal(metersPerSurfaceDegree).multiply(differenceInLatitude); // 4) Multiply the difference in longitude by the cosine of the first point's latitude multiplied by the difference in longitude to get d0Longitude. - BigDecimal firstPointLatitudeInRadians = firstPointLatitude.multiply(new BigDecimal(Math.PI)).divide(new BigDecimal(180), new java.math.MathContext(precision)); - BigDecimal d0Longitude = new BigDecimal(111195).multiply(new BigDecimal(Math.cos(firstPointLatitudeInRadians.doubleValue()))).multiply(differenceInLongitude); - - // 5) Take the square root of d0Latitude squared plus d0Longitude squared to get d0. - BigDecimal d0 = d0Latitude.pow(2).add(d0Longitude.pow(2)).sqrt(new java.math.MathContext(6)); - - // 6) Divide 15 by d0 to get mD. + BigDecimal firstPointLatitudeInRadians = + firstPointLatitude.multiply(new BigDecimal(Math.PI)) + .divide(new BigDecimal(180), new java.math.MathContext(precision)); + BigDecimal d0Longitude = new BigDecimal(111195).multiply( + BigDecimal.valueOf(Math.cos(firstPointLatitudeInRadians.doubleValue()))) + .multiply(differenceInLongitude); + + // 5) Take the square root of d0Latitude squared plus d0Longitude squared to get d0. + BigDecimal d0 = + d0Latitude.pow(2).add(d0Longitude.pow(2)).sqrt(new java.math.MathContext(6)); + + // 6) Divide 15 by d0 to get mD. BigDecimal mD = new BigDecimal(15).divide(d0, new java.math.MathContext(6)); - // 7) Multiply mD by the difference in latitude and add the first point's latitude to get the anchor's latitude. - BigDecimal anchorLatitude = firstPointLatitude.add(mD.multiply(differenceInLatitude)).round(new java.math.MathContext(precision)); + // 7) Multiply mD by the difference in latitude and add the first point's latitude to get the anchor's latitude. + BigDecimal anchorLatitude = firstPointLatitude.add(mD.multiply(differenceInLatitude)) + .round(new java.math.MathContext(precision)); // 8) Multiply mD by the difference in longitude and add the first point's longitude to get the anchor's longitude. - BigDecimal anchorLongitude = firstPointLongitude.add(mD.multiply(differenceInLongitude)).round(new java.math.MathContext(precision)); + BigDecimal anchorLongitude = firstPointLongitude.add(mD.multiply(differenceInLongitude)) + .round(new java.math.MathContext(precision)); - // 9) The anchor coordinate is (anchor latitude, anchor longitude). + // 9) The anchor coordinate is (anchor latitude, anchor longitude). return new Coordinate(anchorLatitude, anchorLongitude); } - private class UnrecognizedDateFormatException extends Exception { - public UnrecognizedDateFormatException(String message) { - super(message); - } - } + private static class UnrecognizedDateFormatException extends Exception { + public UnrecognizedDateFormatException(String message) { + super(message); + } + } + + public static class IdenticalPointsException extends Exception { + public IdenticalPointsException() { + super("The first and second points are identical, cannot calculate anchor point."); + } + } } \ No newline at end of file diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/model/ActiveTimHoldingDeleteModel.java b/cv-data-service-library/src/main/java/com/trihydro/library/model/ActiveTimHoldingDeleteModel.java new file mode 100644 index 000000000..ee83e33c0 --- /dev/null +++ b/cv-data-service-library/src/main/java/com/trihydro/library/model/ActiveTimHoldingDeleteModel.java @@ -0,0 +1,16 @@ +package com.trihydro.library.model; + +import java.util.List; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class ActiveTimHoldingDeleteModel { + + private List ids = null; + + public ActiveTimHoldingDeleteModel(List ids) { + this.ids = ids; + } +} diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/model/Milepost.java b/cv-data-service-library/src/main/java/com/trihydro/library/model/Milepost.java index 8db355381..183e188c4 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/model/Milepost.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/model/Milepost.java @@ -16,6 +16,35 @@ public class Milepost { private BigDecimal longitude; public Milepost() { + + } + + /** + * Constructor to create a new Milepost object with specified attributes. + * + * @param commonName common name of the milepost + * @param milepost milepost value + * @param direction direction of the milepost + * @param latitude latitude of the milepost + * @param longitude longitude of the milepost + */ + public Milepost(String commonName, Double milepost, String direction, BigDecimal latitude, BigDecimal longitude) { + this.commonName = commonName; + this.milepost = milepost; + this.direction = direction; + this.latitude = latitude; + this.longitude = longitude; + } + + /** + * Constructor to create a new Milepost object with specified latitude + * and longitude. Dummy values are used for the other attributes. + * + * @param latitude latitude of the milepost + * @param longitude longitude of the milepost + */ + public Milepost(BigDecimal latitude, BigDecimal longitude) { + this("Unknown", 0.0, "Unknown", latitude, longitude); } /** diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/model/TimUpdateModel.java b/cv-data-service-library/src/main/java/com/trihydro/library/model/TimUpdateModel.java index dd7b2c727..7333b268e 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/model/TimUpdateModel.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/model/TimUpdateModel.java @@ -32,10 +32,10 @@ public class TimUpdateModel extends ActiveTim { private int dataFrameId; private TravelerInfoType frameType; private int durationTime; - private short notUsed1; - private short notUsed; - private short notUsed3; - private short notUsed2; + private short doNotUse2; + private short doNotUse1; + private short doNotUse4; + private short doNotUse3; private ContentEnum dfContent; private String url; @@ -172,12 +172,12 @@ public void setFrameType(TravelerInfoType frameType) { this.frameType = frameType; } - public short getNotUsed() { - return notUsed; + public short getDoNotUse1() { + return doNotUse1; } - public void setNotUsed(short notUsed) { - this.notUsed = notUsed; + public void setDoNotUse1(short doNotUse1) { + this.doNotUse1 = doNotUse1; } public ContentEnum getDfContent() { @@ -188,28 +188,28 @@ public void setDfContent(ContentEnum dfContent) { this.dfContent = dfContent; } - public short getNotUsed2() { - return notUsed2; + public short getDoNotUse3() { + return doNotUse3; } - public void setNotUsed2(short notUsed2) { - this.notUsed2 = notUsed2; + public void setDoNotUse3(short doNotUse3) { + this.doNotUse3 = doNotUse3; } - public short getNotUsed3() { - return notUsed3; + public short getDoNotUse4() { + return doNotUse4; } - public void setNotUsed3(short notUsed3) { - this.notUsed3 = notUsed3; + public void setDoNotUse4(short doNotUse4) { + this.doNotUse4 = doNotUse4; } - public short getNotUsed1() { - return notUsed1; + public short getDoNotUse2() { + return doNotUse2; } - public void setNotUsed1(short notUsed1) { - this.notUsed1 = notUsed1; + public void setDoNotUse2(short doNotUse2) { + this.doNotUse2 = doNotUse2; } public BigDecimal getAnchorLong() { diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/model/WydotTim.java b/cv-data-service-library/src/main/java/com/trihydro/library/model/WydotTim.java index 5d03b0c4d..1f72a5bbe 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/model/WydotTim.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/model/WydotTim.java @@ -7,84 +7,96 @@ public class WydotTim { - @ApiModelProperty(value = "Expected values are I, D, B", required = true) - private String direction; - @ApiModelProperty(required = true) - private Coordinate startPoint; - @ApiModelProperty(required = true) - private Coordinate endPoint; - @ApiModelProperty(value = "The common name for the selected route", required = true) - private String route; - @ApiModelProperty(required = true) - private List itisCodes; - @ApiModelProperty(required = true) - private String clientId; - - public WydotTim() { - - } - - public WydotTim(WydotTim o) { - this.direction = o.direction; - if (o.startPoint != null) - this.startPoint = new Coordinate(o.startPoint.getLatitude(), o.startPoint.getLongitude()); - if (o.endPoint != null) - this.endPoint = new Coordinate(o.endPoint.getLatitude(), o.endPoint.getLongitude()); - this.route = o.route; - if (o.itisCodes != null) - this.itisCodes = new ArrayList<>(o.itisCodes); - this.clientId = o.clientId; - } - - public WydotTim copy() { - return new WydotTim(this); - } - - public String getClientId() { - return this.clientId; - } - - public void setClientId(String clientId) { - this.clientId = clientId; - } - - public List getItisCodes() { - return this.itisCodes; - } - - public void setItisCodes(List itisCodes) { - this.itisCodes = itisCodes; - } - - public String getDirection() { - return this.direction; - } - - public void setDirection(String direction) { - this.direction = direction; - } - - public Coordinate getStartPoint() { - return this.startPoint; - } - - public void setStartPoint(Coordinate startPoint) { - this.startPoint = startPoint; - } - - public Coordinate getEndPoint() { - return this.endPoint; - } - - public void setEndPoint(Coordinate endPoint) { - this.endPoint = endPoint; - } - - public String getRoute() { - return this.route; - } - - public void setRoute(String route) { - this.route = route; - } + @ApiModelProperty(value = "Expected values are I, D, B", required = true) + private String direction; + @ApiModelProperty(required = true) + private Coordinate startPoint; + @ApiModelProperty(required = true) + private Coordinate endPoint; + @ApiModelProperty(value = "The common name for the selected route", required = true) + private String route; + @ApiModelProperty(required = true) + private List itisCodes; + @ApiModelProperty(required = true) + private String clientId; + + public WydotTim() { + + } + + public WydotTim(WydotTim o) { + this.direction = o.direction; + if (o.startPoint != null) { + this.startPoint = + new Coordinate(o.startPoint.getLatitude(), o.startPoint.getLongitude()); + } + if (o.endPoint != null) { + this.endPoint = new Coordinate(o.endPoint.getLatitude(), o.endPoint.getLongitude()); + } + this.route = o.route; + if (o.itisCodes != null) { + this.itisCodes = new ArrayList<>(o.itisCodes); + } + this.clientId = o.clientId; + } + + public WydotTim(TimUpdateModel aTim) { + setClientId(aTim.getClientId()); + setDirection(aTim.getDirection()); + setStartPoint(aTim.getStartPoint()); + setEndPoint(aTim.getEndPoint()); + // note: route and itisCodes are not set in this constructor + } + + public WydotTim copy() { + return new WydotTim(this); + } + + public String getClientId() { + return this.clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public List getItisCodes() { + return this.itisCodes; + } + + public void setItisCodes(List itisCodes) { + this.itisCodes = itisCodes; + } + + public String getDirection() { + return this.direction; + } + + public void setDirection(String direction) { + this.direction = direction; + } + + public Coordinate getStartPoint() { + return this.startPoint; + } + + public void setStartPoint(Coordinate startPoint) { + this.startPoint = startPoint; + } + + public Coordinate getEndPoint() { + return this.endPoint; + } + + public void setEndPoint(Coordinate endPoint) { + this.endPoint = endPoint; + } + + public String getRoute() { + return this.route; + } + + public void setRoute(String route) { + this.route = route; + } } \ No newline at end of file diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/service/ActiveTimHoldingService.java b/cv-data-service-library/src/main/java/com/trihydro/library/service/ActiveTimHoldingService.java index a51605563..68e2f74af 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/service/ActiveTimHoldingService.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/service/ActiveTimHoldingService.java @@ -1,5 +1,6 @@ package com.trihydro.library.service; +import com.trihydro.library.model.ActiveTimHoldingDeleteModel; import java.util.Arrays; import java.util.List; @@ -15,20 +16,35 @@ @Component public class ActiveTimHoldingService extends CvDataServiceLibrary { - public long insertActiveTimHolding(ActiveTimHolding activeTimHolding) { - String url = String.format("%s/active-tim-holding/add", config.getCvRestService()); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity entity = new HttpEntity(activeTimHolding, headers); - ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.POST, entity, - Long.class); - return response.getBody(); - } - - public List getActiveTimHoldingForRsu(String ipv4Address) { - String url = String.format("%s/active-tim-holding/get-rsu/%s", config.getCvRestService(), ipv4Address); - ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, - ActiveTimHolding[].class); - return Arrays.asList(response.getBody()); - } + public void insertActiveTimHolding(ActiveTimHolding activeTimHolding) { + String url = String.format("%s/active-tim-holding/add", config.getCvRestService()); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity<>(activeTimHolding, headers); + restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.POST, entity, Long.class); + } + + public List getActiveTimHoldingForRsu(String ipv4Address) { + String url = String.format("%s/active-tim-holding/get-rsu/%s", config.getCvRestService(), ipv4Address); + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, ActiveTimHolding[].class); + return Arrays.asList(response.getBody()); + } + + public List getAllRecords() { + String url = String.format("%s/active-tim-holding/get-all", config.getCvRestService()); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity<>(headers); + ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.GET, entity, ActiveTimHolding[].class); + return Arrays.asList(response.getBody()); + } + + public boolean deleteActiveTimHoldingRecords(List activeTimHoldingIds) { + String url = String.format("%s/active-tim-holding/delete", config.getCvRestService()); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity<>(new ActiveTimHoldingDeleteModel(activeTimHoldingIds), headers); + ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.DELETE, entity, Boolean.class); + return response.getBody(); + } } \ No newline at end of file diff --git a/cv-data-service-library/src/main/java/com/trihydro/library/service/ActiveTimService.java b/cv-data-service-library/src/main/java/com/trihydro/library/service/ActiveTimService.java index 4d2a710ac..3bb89b706 100644 --- a/cv-data-service-library/src/main/java/com/trihydro/library/service/ActiveTimService.java +++ b/cv-data-service-library/src/main/java/com/trihydro/library/service/ActiveTimService.java @@ -17,214 +17,198 @@ @Component public class ActiveTimService extends CvDataServiceLibrary { - public Boolean updateActiveTim_SatRecordId(Long activeTimId, String satRecordId) { - String url = String.format("%s/active-tim/update-sat-record-id/%d/%s", config.getCvRestService(), activeTimId, - satRecordId); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity entity = new HttpEntity(null, headers); - ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.PUT, entity, - Boolean.class); - return response.getBody(); - } - - public void addItisCodesToActiveTim(ActiveTim activeTim) { - String url = String.format("%s/active-tim/itis-codes/%d", config.getCvRestService(), - activeTim.getActiveTimId()); - ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, Integer[].class); - activeTim.setItisCodes(Arrays.asList(response.getBody())); - } - - public boolean deleteActiveTim(Long activeTimId) { - String url = String.format("%s/active-tim/delete-id/%d", config.getCvRestService(), activeTimId); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity entity = new HttpEntity(null, headers); - ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.DELETE, - entity, Boolean.class); - return response.getBody(); - } - - public boolean deleteActiveTimsById(List activeTimIds) { - String url = String.format("%s/active-tim/delete-ids", config.getCvRestService()); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity> entity = new HttpEntity>(activeTimIds, headers); - ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.DELETE, - entity, Boolean.class); - return response.getBody(); - } - - // utility - public List getActiveTimIndicesByRsu(String rsuTarget) { - String url = String.format("%s/active-tim/indices-rsu/%s", config.getCvRestService(), rsuTarget); - ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, Integer[].class); - return Arrays.asList(response.getBody()); - } - - public List getActiveTimsById(List aTimIds) { - String url = String.format("%s/active-tim/get-by-ids", config.getCvRestService()); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity> entity = new HttpEntity>(aTimIds, headers); - ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.POST, - entity, ActiveTim[].class); - return Arrays.asList(response.getBody()); - } - - public List getActiveTimsByWydotTim(List wydotTims, Long timTypeId) { - String url = String.format("%s/active-tim/get-by-wydot-tim/%d", config.getCvRestService(), timTypeId); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity> entity = new HttpEntity>(wydotTims, headers); - ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.POST, - entity, ActiveTim[].class); - return Arrays.asList(response.getBody()); - } - - // get Active TIMs by client ID direction - public List getActiveTimsByClientIdDirection(String clientId, Long timTypeId, String direction) { - String url = String.format("%s/active-tim/client-id-direction/%s/%d", config.getCvRestService(), clientId, - timTypeId); - if (direction != null) { - url += "/" + direction; - } - ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, - ActiveTim[].class); - return Arrays.asList(response.getBody()); - } - - // get buffers for RW TIMs - public List getBufferTimsByClientId(String clientId) { - String url = String.format("%s/active-tim/buffer-tims/%s", config.getCvRestService(), clientId); - ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, - ActiveTim[].class); - return Arrays.asList(response.getBody()); - } - - public List getExpiredActiveTims() { - ResponseEntity response = restTemplateProvider.GetRestTemplate() - .getForEntity(config.getCvRestService() + "/active-tim/expired", ActiveTim[].class); - return Arrays.asList(response.getBody()); - } - - // for GETs - public List getActivesTimByType(Long timTypeId) { - String url = String.format("%s/active-tim/tim-type-id/%d", config.getCvRestService(), timTypeId); - ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, - ActiveTim[].class); - return Arrays.asList(response.getBody()); - } - - // get Active TIMs by client ID direction - public ActiveTim getActiveRsuTim(ActiveRsuTimQueryModel artqm) { - String url = String.format("%s/active-tim/active-rsu-tim", config.getCvRestService()); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity entity = new HttpEntity(artqm, headers); - ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.POST, - entity, ActiveTim.class); - return response.getBody(); - } - - /** - * Fetch all ActiveTims for RSUs from cv-data-controller - * - * @return List of ActiveTims (including RSU address and index) - */ - public List getActiveRsuTims() { - return getActiveRsuTims(config.getCvRestService()); - } - - /** - * Fetch all ActiveTims for RSUs from the specified endpoint - * - * @return List of ActiveTims (including RSU address and index) - */ - public List getActiveRsuTims(String endpoint) { - ResponseEntity response = restTemplateProvider.GetRestTemplate() - .getForEntity(endpoint + "/active-tim/active-rsu-tims", ActiveTim[].class); - return Arrays.asList(response.getBody()); - } - - /** - * Calls out to the cv-data-controller REST function to fetch expiring TIMs - * - * @return List of TimUpdateModel representing all TIMs expiring within 1 day - */ - public List getExpiringActiveTims() { - ResponseEntity response = restTemplateProvider.GetRestTemplate() - .getForEntity(config.getCvRestService() + "/active-tim/expiring", TimUpdateModel[].class); - return Arrays.asList(response.getBody()); - } - - public List getActiveTimsMissingItisCodes() { - ResponseEntity response = restTemplateProvider.GetRestTemplate() - .getForEntity(config.getCvRestService() + "/active-tim/missing-itis", TimUpdateModel[].class); - return Arrays.asList(response.getBody()); - } - - public List getActiveTimsNotSent() { - ResponseEntity response = restTemplateProvider.GetRestTemplate() - .getForEntity(config.getCvRestService() + "/active-tim/not-sent", TimUpdateModel[].class); - return Arrays.asList(response.getBody()); - } - - public List getActiveTimsForSDX() { - ResponseEntity response = restTemplateProvider.GetRestTemplate() - .getForEntity(config.getCvRestService() + "/active-tim/all-sdx", ActiveTim[].class); - return Arrays.asList(response.getBody()); - } - - public List getActiveTimsWithItisCodes(boolean excludeVslAndParking) { - ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity( - config.getCvRestService() + "/active-tim/all-with-itis?excludeVslAndParking=" + excludeVslAndParking, - ActiveTim[].class); - return Arrays.asList(response.getBody()); - } - - public Boolean updateActiveTimExpiration(String packetID, String expDate) { - String url = String.format("%s/active-tim/update-expiration/%s/%s", config.getCvRestService(), packetID, - expDate); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity entity = new HttpEntity(null, headers); - ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.PUT, entity, - Boolean.class); - return response.getBody(); - } - - public String getMinExpiration(String packetID, String expDate) { - /// get-min-expiration/{packetID}/{startDate}/{expDate} - String url = String.format("%s/active-tim/get-min-expiration/%s/%s", config.getCvRestService(), packetID, - expDate); - ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, String.class); - return response.getBody(); - } - - public TimUpdateModel getUpdateModelFromActiveTimId(Long activeTimId) { - String url = String.format("%s/active-tim/update-model/%d", config.getCvRestService(), activeTimId); - ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, - TimUpdateModel.class); - return response.getBody(); - } - - public boolean resetActiveTimsExpirationDate(List activeTimIds) { - String url = String.format("%s/active-tim/reset-expiration-date", config.getCvRestService()); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity> entity = new HttpEntity>(activeTimIds, headers); - ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.PUT, entity, - Boolean.class); - return response.getBody(); - } - - public void markForDeletion(Long activeTimId) { - String url = String.format("%s/active-tim/mark-for-deletion/%d", config.getCvRestService(), activeTimId); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity entity = new HttpEntity(null, headers); - ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.PUT, entity, - Boolean.class); - } + public Boolean updateActiveTim_SatRecordId(Long activeTimId, String satRecordId) { + String url = String.format("%s/active-tim/update-sat-record-id/%d/%s", config.getCvRestService(), activeTimId, satRecordId); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity(null, headers); + ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.PUT, entity, Boolean.class); + return response.getBody(); + } + + public void addItisCodesToActiveTim(ActiveTim activeTim) { + String url = String.format("%s/active-tim/itis-codes/%d", config.getCvRestService(), activeTim.getActiveTimId()); + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, Integer[].class); + activeTim.setItisCodes(Arrays.asList(response.getBody())); + } + + public boolean deleteActiveTim(Long activeTimId) { + String url = String.format("%s/active-tim/delete-id/%d", config.getCvRestService(), activeTimId); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity(null, headers); + ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.DELETE, entity, Boolean.class); + return response.getBody(); + } + + public boolean deleteActiveTimsById(List activeTimIds) { + String url = String.format("%s/active-tim/delete-ids", config.getCvRestService()); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity> entity = new HttpEntity>(activeTimIds, headers); + ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.DELETE, entity, Boolean.class); + return response.getBody(); + } + + // utility + public List getActiveTimIndicesByRsu(String rsuTarget) { + String url = String.format("%s/active-tim/indices-rsu/%s", config.getCvRestService(), rsuTarget); + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, Integer[].class); + return Arrays.asList(response.getBody()); + } + + public List getActiveTimsById(List aTimIds) { + String url = String.format("%s/active-tim/get-by-ids", config.getCvRestService()); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity> entity = new HttpEntity>(aTimIds, headers); + ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.POST, entity, ActiveTim[].class); + return Arrays.asList(response.getBody()); + } + + public List getActiveTimsByWydotTim(List wydotTims, Long timTypeId) { + String url = String.format("%s/active-tim/get-by-wydot-tim/%d", config.getCvRestService(), timTypeId); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity> entity = new HttpEntity>(wydotTims, headers); + ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.POST, entity, ActiveTim[].class); + return Arrays.asList(response.getBody()); + } + + // get Active TIMs by client ID direction + public List getActiveTimsByClientIdDirection(String clientId, Long timTypeId, String direction) { + String url = String.format("%s/active-tim/client-id-direction/%s/%d", config.getCvRestService(), clientId, timTypeId); + if (direction != null) { + url += "/" + direction; + } + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, ActiveTim[].class); + return Arrays.asList(response.getBody()); + } + + // get buffers for RW TIMs + public List getBufferTimsByClientId(String clientId) { + String url = String.format("%s/active-tim/buffer-tims/%s", config.getCvRestService(), clientId); + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, ActiveTim[].class); + return Arrays.asList(response.getBody()); + } + + public List getExpiredActiveTims(int limit) { + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(config.getCvRestService() + "/active-tim/expired?limit=" + limit, ActiveTim[].class); + return Arrays.asList(response.getBody()); + } + + // for GETs + public List getActivesTimByType(Long timTypeId) { + String url = String.format("%s/active-tim/tim-type-id/%d", config.getCvRestService(), timTypeId); + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, ActiveTim[].class); + return Arrays.asList(response.getBody()); + } + + // get Active TIMs by client ID direction + public ActiveTim getActiveRsuTim(ActiveRsuTimQueryModel artqm) { + String url = String.format("%s/active-tim/active-rsu-tim", config.getCvRestService()); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity(artqm, headers); + ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.POST, entity, ActiveTim.class); + return response.getBody(); + } + + /** + * Fetch all ActiveTims for RSUs from cv-data-controller + * + * @return List of ActiveTims (including RSU address and index) + */ + public List getActiveRsuTims() { + return getActiveRsuTims(config.getCvRestService()); + } + + /** + * Fetch all ActiveTims for RSUs from the specified endpoint + * + * @return List of ActiveTims (including RSU address and index) + */ + public List getActiveRsuTims(String endpoint) { + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(endpoint + "/active-tim/active-rsu-tims", ActiveTim[].class); + return Arrays.asList(response.getBody()); + } + + /** + * Calls out to the cv-data-controller REST function to fetch expiring TIMs + * + * @return List of TimUpdateModel representing all TIMs expiring within 1 day + */ + public List getExpiringActiveTims() { + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(config.getCvRestService() + "/active-tim/expiring", TimUpdateModel[].class); + return Arrays.asList(response.getBody()); + } + + public List getActiveTimsMissingItisCodes() { + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(config.getCvRestService() + "/active-tim/missing-itis", TimUpdateModel[].class); + return Arrays.asList(response.getBody()); + } + + public List getActiveTimsNotSent() { + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(config.getCvRestService() + "/active-tim/not-sent", TimUpdateModel[].class); + return Arrays.asList(response.getBody()); + } + + public List getActiveTimsForSDX() { + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(config.getCvRestService() + "/active-tim/all-sdx", ActiveTim[].class); + return Arrays.asList(response.getBody()); + } + + public List getActiveTimsWithItisCodes(boolean excludeVslAndParking) { + ResponseEntity response = + restTemplateProvider.GetRestTemplate().getForEntity(config.getCvRestService() + "/active-tim/all-with-itis?excludeVslAndParking=" + excludeVslAndParking, ActiveTim[].class); + return Arrays.asList(response.getBody()); + } + + public Boolean updateActiveTimExpiration(String packetID, String expDate) { + String url = String.format("%s/active-tim/update-expiration/%s/%s", config.getCvRestService(), packetID, expDate); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity(null, headers); + ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.PUT, entity, Boolean.class); + return response.getBody(); + } + + public String getMinExpiration(String packetID, String expDate) { + /// get-min-expiration/{packetID}/{startDate}/{expDate} + String url = String.format("%s/active-tim/get-min-expiration/%s/%s", config.getCvRestService(), packetID, expDate); + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, String.class); + return response.getBody(); + } + + public TimUpdateModel getUpdateModelFromActiveTimId(Long activeTimId) { + String url = String.format("%s/active-tim/update-model/%d", config.getCvRestService(), activeTimId); + ResponseEntity response = restTemplateProvider.GetRestTemplate().getForEntity(url, TimUpdateModel.class); + return response.getBody(); + } + + public boolean resetActiveTimsExpirationDate(List activeTimIds) { + String url = String.format("%s/active-tim/reset-expiration-date", config.getCvRestService()); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity> entity = new HttpEntity>(activeTimIds, headers); + ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.PUT, entity, Boolean.class); + return response.getBody(); + } + + public void markForDeletion(Long activeTimId) { + String url = String.format("%s/active-tim/mark-for-deletion/%d", config.getCvRestService(), activeTimId); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity(null, headers); + ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.PUT, entity, Boolean.class); + } + + public List getAllRecords() { + String url = String.format("%s/active-tim/all", config.getCvRestService()); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity<>(headers); + ResponseEntity response = restTemplateProvider.GetRestTemplate().exchange(url, HttpMethod.GET, entity, ActiveTim[].class); + return Arrays.asList(response.getBody()); + } } \ No newline at end of file diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/DbInteractionsTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/DbInteractionsTest.java index 04c143608..89bd4f24a 100644 --- a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/DbInteractionsTest.java +++ b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/DbInteractionsTest.java @@ -3,68 +3,76 @@ import com.trihydro.library.model.DbInteractionsProps; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import org.mockito.MockitoAnnotations; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; class DbInteractionsTest { + private DbInteractions uut; @Mock private DbInteractionsProps dbConfig; @Mock - private Utility utility; + private EmailHelper emailHelper; @Mock - private EmailHelper emailHelper; + private PreparedStatement preparedStatement; - @InjectMocks - private DbInteractions dbInteractions; + @Mock + private ResultSet resultSet; @BeforeEach void setUp() { MockitoAnnotations.initMocks(this); + + when(dbConfig.getDbUrl()).thenReturn("jdbc:postgresql://localhost:5432/test"); + when(dbConfig.getDbUsername()).thenReturn("test"); + when(dbConfig.getDbPassword()).thenReturn("test"); + when(dbConfig.getMaximumPoolSize()).thenReturn(10); + when(dbConfig.getConnectionTimeout()).thenReturn(1000); + uut = new DbInteractions(dbConfig, emailHelper); } @Test - void updateOrDelete_executesUpdate() throws SQLException { - PreparedStatement preparedStatement = mock(PreparedStatement.class); + void testUpdateOrDelete_WhenExecuted_ShouldReturnTrue() throws SQLException { when(preparedStatement.executeUpdate()).thenReturn(1); - boolean result = dbInteractions.updateOrDelete(preparedStatement); + boolean result = uut.updateOrDelete(preparedStatement); assertTrue(result); + verify(preparedStatement, times(1)).executeUpdate(); } @Test - void deleteWithPossibleZero_executesUpdate() throws SQLException { - PreparedStatement preparedStatement = mock(PreparedStatement.class); + void testDeleteWithPossibleZero_WhenNoRowsAffected_ShouldReturnTrue() throws SQLException { when(preparedStatement.executeUpdate()).thenReturn(0); - boolean result = dbInteractions.deleteWithPossibleZero(preparedStatement); + boolean result = uut.deleteWithPossibleZero(preparedStatement); assertTrue(result); + verify(preparedStatement, times(1)).executeUpdate(); } @Test - void executeAndLog_generatesKey() throws SQLException { - PreparedStatement preparedStatement = mock(PreparedStatement.class); - ResultSet resultSet = mock(ResultSet.class); + void testExecuteAndLog_WhenKeyGenerated_ShouldReturnGeneratedKey() throws SQLException { when(preparedStatement.executeUpdate()).thenReturn(1); when(preparedStatement.getGeneratedKeys()).thenReturn(resultSet); when(resultSet.next()).thenReturn(true); when(resultSet.getLong(1)).thenReturn(1L); - Long id = dbInteractions.executeAndLog(preparedStatement, "type"); + Long id = uut.executeAndLog(preparedStatement, "type"); assertNotNull(id); assertEquals(1L, id); + verify(preparedStatement, times(1)).executeUpdate(); + verify(preparedStatement, times(1)).getGeneratedKeys(); + verify(resultSet, times(1)).next(); } } \ No newline at end of file diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/JsonToJavaConverterTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/JsonToJavaConverterTest.java index 131c96bc0..e89ca7501 100644 --- a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/JsonToJavaConverterTest.java +++ b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/JsonToJavaConverterTest.java @@ -7,8 +7,6 @@ import java.nio.file.Path; import java.nio.file.Paths; -import com.fasterxml.jackson.databind.JsonNode; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -22,8 +20,6 @@ import us.dot.its.jpo.ode.model.ReceivedMessageDetails; import us.dot.its.jpo.ode.model.RxSource; import us.dot.its.jpo.ode.model.SerialId; -import us.dot.its.jpo.ode.plugin.j2735.J2735SupplementalVehicleExtensions; -import us.dot.its.jpo.ode.plugin.j2735.J2735VehicleSafetyExtensions; import us.dot.its.jpo.ode.plugin.j2735.OdePosition3D; import us.dot.its.jpo.ode.plugin.j2735.OdeTravelerInformationMessage; import us.dot.its.jpo.ode.plugin.j2735.OdeTravelerInformationMessage.DataFrame.Region.Circle; diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/TimGenerationHelperTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/TimGenerationHelperTest.java index c2904be6d..3f65b8a7d 100644 --- a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/TimGenerationHelperTest.java +++ b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/TimGenerationHelperTest.java @@ -4,12 +4,14 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; import java.math.BigDecimal; import java.sql.Timestamp; import java.time.Instant; @@ -44,8 +46,8 @@ import com.trihydro.library.service.SdwService; import com.trihydro.library.service.TimGenerationProps; -import org.junit.After; -import org.junit.Before; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -56,7 +58,7 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -public class TimGenerationHelperTest { +class TimGenerationHelperTest { @Mock private Utility mockUtility; @Mock @@ -85,8 +87,10 @@ public class TimGenerationHelperTest { private SnmpHelper mockSnmpHelper; @Mock private RegionNameTrimmer mockRegionNameTrimmer; + @Mock + private IdenticalPointsExceptionHandler mockIdenticalPointsExceptionHandler; - private static Long activeTimId = -1l; + private static final Long activeTimId = -1L; private TimUpdateModel tum; @InjectMocks @@ -95,12 +99,12 @@ public class TimGenerationHelperTest { @Captor private ArgumentCaptor timCaptor; - @Before + @BeforeEach public void setup() { TimeZone.setDefault(TimeZone.getTimeZone("UTC")); } - @After + @AfterEach public void teardown() { TimeZone.setDefault(TimeZone.getTimeZone(java.time.ZoneId.systemDefault())); } @@ -108,7 +112,7 @@ public void teardown() { @Test public void resubmitToOde_EmptyList() { // Arrange - List activeTimIds = new ArrayList(); + List activeTimIds = new ArrayList<>(); // Act var exceptions = uut.resubmitToOde(activeTimIds); @@ -131,7 +135,7 @@ public void resubmitToOde_NullList() { @Test public void resubmitToOde_NullTum() { // Arrange - List activeTimIds = new ArrayList(); + List activeTimIds = new ArrayList<>(); activeTimIds.add(-1L); doReturn(null).when(mockActiveTimService).getUpdateModelFromActiveTimId(any()); @@ -139,9 +143,9 @@ public void resubmitToOde_NullTum() { var exceptions = uut.resubmitToOde(activeTimIds); // Assert - verifyNoInteractions(mockDataFrameService, mockPathNodeXYService, mockMilepostService, mockMilepostReduction, - mockRegionService, mockRsuService, mockConfig, mockOdeService, mockActiveTimHoldingService, - mockSdwService); + verifyNoInteractions(mockDataFrameService, mockPathNodeXYService, mockMilepostService, + mockMilepostReduction, mockRegionService, mockRsuService, mockConfig, mockOdeService, + mockActiveTimHoldingService, mockSdwService); Assertions.assertEquals(1, exceptions.size()); var ex = exceptions.get(0); String exMsg = "Failed to get Update Model from active tim"; @@ -151,11 +155,11 @@ public void resubmitToOde_NullTum() { @Test public void resubmitToOde_NoMileposts() { // Arrange - List activeTimIds = new ArrayList(); + List activeTimIds = new ArrayList<>(); activeTimIds.add(-1L); setupActiveTimModel(); - List mps = new ArrayList(); + List mps = new ArrayList<>(); doReturn(mps).when(mockMilepostService).getMilepostsByStartEndPointDirection(any()); // Act @@ -164,29 +168,31 @@ public void resubmitToOde_NoMileposts() { // Assert Assertions.assertEquals(1, exceptions.size()); var ex = exceptions.get(0); - String exMsg = String.format( - "Unable to resubmit TIM, less than 2 mileposts found for Active_Tim %d", activeTimId); + String exMsg = + String.format("Unable to resubmit TIM, less than 2 mileposts found for Active_Tim %d", + activeTimId); Assertions.assertEquals(new ResubmitTimException(activeTimId, exMsg), ex); - verifyNoInteractions(mockDataFrameService, mockPathNodeXYService, mockRegionService, mockRsuService, - mockOdeService, mockActiveTimHoldingService, mockSdwService); + verifyNoInteractions(mockDataFrameService, mockPathNodeXYService, mockRegionService, + mockRsuService, mockOdeService, mockActiveTimHoldingService, mockSdwService); verify(mockMilepostService).getMilepostsByStartEndPointDirection(any()); verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction); } @Test - public void resubmitToOde_DataFrameException() { + public void resubmitToOde_DataFrameException() throws Utility.IdenticalPointsException { // Arrange - List activeTimIds = new ArrayList(); + List activeTimIds = new ArrayList<>(); activeTimIds.add(-1L); setupActiveTimModel(); setupMilepostReturn(); tum.setRoute("I 80"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act var exceptions = uut.resubmitToOde(activeTimIds); @@ -198,8 +204,8 @@ public void resubmitToOde_DataFrameException() { Assertions.assertEquals(new ResubmitTimException(activeTimId, exMsg), ex); verify(mockDataFrameService).getItisCodesForDataFrameId(any()); - verifyNoInteractions(mockPathNodeXYService, mockRegionService, mockOdeService, mockActiveTimHoldingService, - mockSdwService, mockRsuService); + verifyNoInteractions(mockPathNodeXYService, mockRegionService, mockOdeService, + mockActiveTimHoldingService, mockSdwService, mockRsuService); verify(mockMilepostService).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); @@ -207,23 +213,25 @@ public void resubmitToOde_DataFrameException() { } @Test - public void resubmitToOde_RsuException() { + public void resubmitToOde_RsuException() throws Utility.IdenticalPointsException { // Arrange - List activeTimIds = new ArrayList(); + List activeTimIds = new ArrayList<>(); activeTimIds.add(-1L); setupActiveTimModel(); setupMilepostReturn(); tum.setRoute("I 80"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - String[] rsuRoutes = new String[] { "I 80" }; + String[] rsuRoutes = new String[] {"I 80"}; doReturn(rsuRoutes).when(mockConfig).getRsuRoutes(); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act var exceptions = uut.resubmitToOde(activeTimIds); @@ -237,27 +245,28 @@ public void resubmitToOde_RsuException() { verify(mockRsuService).getFullRsusTimIsOn(any()); verify(mockRsuService).getRsusByLatLong(any(), any(), any(), any()); verify(mockDataFrameService).getItisCodesForDataFrameId(any()); - verifyNoInteractions(mockPathNodeXYService, mockRegionService, mockOdeService, mockActiveTimHoldingService, - mockSdwService); + verifyNoInteractions(mockPathNodeXYService, mockRegionService, mockOdeService, + mockActiveTimHoldingService, mockSdwService); verify(mockMilepostService).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, mockRsuService); + verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, + mockRsuService); } @Test - public void resubmitToOde_RsuExistingSuccess() { + public void resubmitToOde_RsuExistingSuccess() throws Utility.IdenticalPointsException { // Arrange - List activeTimIds = new ArrayList(); + List activeTimIds = new ArrayList<>(); activeTimIds.add(-1L); setupActiveTimModel(); setupMilepostReturn(); tum.setRoute("I 80"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - String[] rsuRoutes = new String[] { "I 80" }; + String[] rsuRoutes = new String[] {"I 80"}; doReturn(rsuRoutes).when(mockConfig).getRsuRoutes(); List wydotRsus = new ArrayList<>(); @@ -265,9 +274,11 @@ public void resubmitToOde_RsuExistingSuccess() { wydotRsuTim.setIndex(-1); wydotRsus.add(wydotRsuTim); doReturn(wydotRsus).when(mockRsuService).getFullRsusTimIsOn(any()); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act var exceptions = uut.resubmitToOde(activeTimIds); @@ -278,38 +289,42 @@ public void resubmitToOde_RsuExistingSuccess() { verify(mockDataFrameService).getItisCodesForDataFrameId(any()); verify(mockOdeService).deleteTimFromRsu(any(), any()); verify(mockOdeService).sendNewTimToRsu(any()); - verifyNoInteractions(mockPathNodeXYService, mockRegionService, mockActiveTimHoldingService, mockSdwService); + verifyNoInteractions(mockPathNodeXYService, mockRegionService, mockActiveTimHoldingService, + mockSdwService); verify(mockMilepostService).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, mockRsuService, - mockOdeService); + verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, + mockRsuService, mockOdeService); } @Test - public void resubmitToOde_RsuNewFailTimQuery() { + public void resubmitToOde_RsuNewFailTimQuery() throws Utility.IdenticalPointsException { // Arrange - List activeTimIds = new ArrayList(); + List activeTimIds = new ArrayList<>(); activeTimIds.add(-1L); setupActiveTimModel(); setupMilepostReturn(); tum.setRoute("I 80"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - String[] rsuRoutes = new String[] { "I 80" }; + String[] rsuRoutes = new String[] {"I 80"}; doReturn(rsuRoutes).when(mockConfig).getRsuRoutes(); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); List dbRsus = new ArrayList<>(); var rsu = new WydotRsu(); rsu.setRsuTarget("10.10.10.10"); dbRsus.add(rsu); doReturn(dbRsus).when(mockRsuService).getRsusByLatLong(any(), any(), any(), any()); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); - when(mockOdeService.submitTimQuery(isA(WydotRsu.class), isA(Integer.class))).thenReturn(null); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); + when(mockOdeService.submitTimQuery(isA(WydotRsu.class), isA(Integer.class))).thenReturn( + null); // Act var exceptions = uut.resubmitToOde(activeTimIds); @@ -318,7 +333,8 @@ public void resubmitToOde_RsuNewFailTimQuery() { Gson gson = new Gson(); Assertions.assertEquals(1, exceptions.size()); var ex = exceptions.get(0); - var exMsg = "Returning without sending TIM to RSU. submitTimQuery failed for RSU " + gson.toJson(rsu); + var exMsg = "Returning without sending TIM to RSU. submitTimQuery failed for RSU " + + gson.toJson(rsu); exMsg += "\n"; Assertions.assertEquals(new ResubmitTimException(activeTimId, exMsg), ex); @@ -330,23 +346,23 @@ public void resubmitToOde_RsuNewFailTimQuery() { verify(mockMilepostService).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, mockRsuService, - mockOdeService, mockActiveTimHoldingService); + verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, + mockRsuService, mockOdeService, mockActiveTimHoldingService); } @Test - public void resubmitToOde_RsuNewFailIndices() { + public void resubmitToOde_RsuNewFailIndices() throws Utility.IdenticalPointsException { // Arrange - List activeTimIds = new ArrayList(); + List activeTimIds = new ArrayList<>(); activeTimIds.add(-1L); setupActiveTimModel(); setupMilepostReturn(); tum.setRoute("I 80"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - String[] rsuRoutes = new String[] { "I 80" }; + String[] rsuRoutes = new String[] {"I 80"}; doReturn(rsuRoutes).when(mockConfig).getRsuRoutes(); List dbRsus = new ArrayList<>(); @@ -354,11 +370,14 @@ public void resubmitToOde_RsuNewFailIndices() { rsu.setRsuTarget("10.10.10.10"); dbRsus.add(rsu); doReturn(dbRsus).when(mockRsuService).getRsusByLatLong(any(), any(), any(), any()); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); - when(mockOdeService.submitTimQuery(isA(WydotRsu.class), isA(Integer.class))).thenReturn(new TimQuery()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); + when(mockOdeService.submitTimQuery(isA(WydotRsu.class), isA(Integer.class))).thenReturn( + new TimQuery()); when(mockOdeService.findFirstAvailableIndexWithRsuIndex(any())).thenReturn(null); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act var exceptions = uut.resubmitToOde(activeTimIds); @@ -381,23 +400,23 @@ public void resubmitToOde_RsuNewFailIndices() { verify(mockMilepostService).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, mockRsuService, - mockOdeService, mockActiveTimHoldingService); + verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, + mockRsuService, mockOdeService, mockActiveTimHoldingService); } @Test - public void resubmitToOde_RsuNewInsertFail() { + public void resubmitToOde_RsuNewInsertFail() throws Utility.IdenticalPointsException { // Arrange - List activeTimIds = new ArrayList(); + List activeTimIds = new ArrayList<>(); activeTimIds.add(-1L); setupActiveTimModel(); setupMilepostReturn(); tum.setRoute("I 80"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - String[] rsuRoutes = new String[] { "I 80" }; + String[] rsuRoutes = new String[] {"I 80"}; doReturn(rsuRoutes).when(mockConfig).getRsuRoutes(); List dbRsus = new ArrayList<>(); @@ -405,12 +424,15 @@ public void resubmitToOde_RsuNewInsertFail() { rsu.setRsuTarget("10.10.10.10"); dbRsus.add(rsu); doReturn(dbRsus).when(mockRsuService).getRsusByLatLong(any(), any(), any(), any()); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); - when(mockOdeService.submitTimQuery(isA(WydotRsu.class), isA(Integer.class))).thenReturn(new TimQuery()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); + when(mockOdeService.submitTimQuery(isA(WydotRsu.class), isA(Integer.class))).thenReturn( + new TimQuery()); when(mockOdeService.findFirstAvailableIndexWithRsuIndex(any())).thenReturn(1); doReturn("exception").when(mockOdeService).sendNewTimToRsu(any()); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act var exceptions = uut.resubmitToOde(activeTimIds); @@ -434,23 +456,23 @@ public void resubmitToOde_RsuNewInsertFail() { verify(mockMilepostService).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, mockRsuService, - mockOdeService, mockActiveTimHoldingService); + verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, + mockRsuService, mockOdeService, mockActiveTimHoldingService); } @Test - public void resubmitToOde_RsuNewSuccess() { + public void resubmitToOde_RsuNewSuccess() throws Utility.IdenticalPointsException { // Arrange - List activeTimIds = new ArrayList(); + List activeTimIds = new ArrayList<>(); activeTimIds.add(-1L); setupActiveTimModel(); setupMilepostReturn(); tum.setRoute("I 80"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - String[] rsuRoutes = new String[] { "I 80" }; + String[] rsuRoutes = new String[] {"I 80"}; doReturn(rsuRoutes).when(mockConfig).getRsuRoutes(); List dbRsus = new ArrayList<>(); @@ -458,11 +480,14 @@ public void resubmitToOde_RsuNewSuccess() { rsu.setRsuTarget("10.10.10.10"); dbRsus.add(rsu); doReturn(dbRsus).when(mockRsuService).getRsusByLatLong(any(), any(), any(), any()); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); - when(mockOdeService.submitTimQuery(isA(WydotRsu.class), isA(Integer.class))).thenReturn(new TimQuery()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); + when(mockOdeService.submitTimQuery(isA(WydotRsu.class), isA(Integer.class))).thenReturn( + new TimQuery()); when(mockOdeService.findFirstAvailableIndexWithRsuIndex(any())).thenReturn(1); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act var exceptions = uut.resubmitToOde(activeTimIds); @@ -481,28 +506,30 @@ public void resubmitToOde_RsuNewSuccess() { verify(mockMilepostService).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, mockRsuService, - mockOdeService, mockActiveTimHoldingService); + verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, + mockRsuService, mockOdeService, mockActiveTimHoldingService); } @Test - public void resubmitToOde_SdxNewFail() { + public void resubmitToOde_SdxNewFail() throws Utility.IdenticalPointsException { // Arrange - List activeTimIds = new ArrayList(); + List activeTimIds = new ArrayList<>(); activeTimIds.add(-1L); setupActiveTimModel(); setupMilepostReturn(); tum.setRoute("I 80"); tum.setSatRecordId("AA123456"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); doReturn("exception").when(mockOdeService).updateTimOnSdw(any()); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act var exceptions = uut.resubmitToOde(activeTimIds); @@ -511,32 +538,34 @@ public void resubmitToOde_SdxNewFail() { Assertions.assertEquals(1, exceptions.size()); var ex = exceptions.get(0); Assertions.assertEquals(new ResubmitTimException(activeTimId, "exception"), ex); - + verify(mockActiveTimHoldingService).insertActiveTimHolding(any()); verifyNoInteractions(mockPathNodeXYService); verify(mockMilepostService).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, mockRsuService, - mockOdeService, mockActiveTimHoldingService); + verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, + mockRsuService, mockOdeService, mockActiveTimHoldingService); } @Test - public void resubmitToOde_SdxNewSuccess() { + public void resubmitToOde_SdxNewSuccess() throws Utility.IdenticalPointsException { // Arrange - List activeTimIds = new ArrayList(); + List activeTimIds = new ArrayList<>(); activeTimIds.add(-1L); setupActiveTimModel(); setupMilepostReturn(); tum.setRoute("I 80"); tum.setSatRecordId("satRecordId"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); doReturn("").when(mockOdeService).updateTimOnSdw(any()); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act var exceptions = uut.resubmitToOde(activeTimIds); @@ -545,18 +574,19 @@ public void resubmitToOde_SdxNewSuccess() { Assertions.assertEquals(0, exceptions.size()); verifyNoInteractions(mockPathNodeXYService); verify(mockOdeService).updateTimOnSdw(any()); - + verify(mockActiveTimHoldingService).insertActiveTimHolding(any()); verify(mockMilepostService).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, mockRsuService, - mockOdeService, mockActiveTimHoldingService); + verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, + mockRsuService, mockOdeService, mockActiveTimHoldingService); } @Test - public void resetTimStartTimAndResubmitToOde_updatesStartTime() { + public void resetTimStartTimAndResubmitToOde_updatesStartTime() + throws Utility.IdenticalPointsException { // Arrange - List activeTimIds = new ArrayList(); + List activeTimIds = new ArrayList<>(); activeTimIds.add(-1L); setupActiveTimModel(); setupMilepostReturn(); @@ -569,16 +599,19 @@ public void resetTimStartTimAndResubmitToOde_updatesStartTime() { var oldStartTime = Instant.now().minusSeconds(1); tum.setStartDate_Timestamp(new Timestamp(oldStartTime.toEpochMilli())); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); doReturn("").when(mockOdeService).updateTimOnSdw(any()); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act uut.resetTimStartTimeAndResubmitToOde(activeTimIds); // Assert verify(mockOdeService).updateTimOnSdw(timCaptor.capture()); + verify(mockActiveTimHoldingService).insertActiveTimHolding(any()); var timSent = timCaptor.getValue(); var dataFrame = timSent.getTim().getDataframes()[0]; @@ -592,9 +625,9 @@ public void resetTimStartTimAndResubmitToOde_updatesStartTime() { } @Test - public void resubmitToOde_usesOldStartTime() { + public void resubmitToOde_usesOldStartTime() throws Utility.IdenticalPointsException { // Arrange - List activeTimIds = new ArrayList(); + List activeTimIds = new ArrayList<>(); activeTimIds.add(-1L); setupActiveTimModel(); setupMilepostReturn(); @@ -609,17 +642,20 @@ public void resubmitToOde_usesOldStartTime() { tum.setEndDateTime("2021-01-01T01:00:00.000Z"); tum.setStartDate_Timestamp(new Timestamp(originalStartTime.toEpochMilli())); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); doReturn("").when(mockOdeService).updateTimOnSdw(any()); doReturn(60).when(mockUtility).getMinutesDurationBetweenTwoDates(anyString(), anyString()); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act uut.resubmitToOde(activeTimIds); // Assert verify(mockOdeService).updateTimOnSdw(timCaptor.capture()); + verify(mockActiveTimHoldingService).insertActiveTimHolding(any()); var timSent = timCaptor.getValue(); var dataFrame = timSent.getTim().getDataframes()[0]; @@ -632,9 +668,9 @@ public void resubmitToOde_usesOldStartTime() { } @Test - public void c_updatesDurationTimeToFiveMinutes() { + public void c_updatesDurationTimeToFiveMinutes() throws Utility.IdenticalPointsException { // Arrange - List activeTimIds = new ArrayList(); + List activeTimIds = new ArrayList<>(); activeTimIds.add(-1L); setupActiveTimModel(); setupMilepostReturn(); @@ -648,16 +684,19 @@ public void c_updatesDurationTimeToFiveMinutes() { tum.setEndDateTime("2021-01-01T01:00:00.000Z"); tum.setStartDate_Timestamp(new Timestamp(originalStartTime.toEpochMilli())); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); doReturn("").when(mockOdeService).updateTimOnSdw(any()); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act uut.expireTimAndResubmitToOde(activeTimIds); // Assert verify(mockOdeService).updateTimOnSdw(timCaptor.capture()); + verify(mockActiveTimHoldingService).insertActiveTimHolding(any()); var timSent = timCaptor.getValue(); var dataFrame = timSent.getTim().getDataframes()[0]; @@ -668,26 +707,28 @@ public void c_updatesDurationTimeToFiveMinutes() { } @Test - public void resubmitToOde_SdxExistingFail() { + public void resubmitToOde_SdxExistingFail() throws Utility.IdenticalPointsException { // Arrange - List activeTimIds = new ArrayList(); + List activeTimIds = new ArrayList<>(); activeTimIds.add(-1L); setupActiveTimModel(); setupMilepostReturn(); tum.setRoute("I 80"); tum.setSatRecordId("satRecordId"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); doReturn("exception").when(mockOdeService).updateTimOnSdw(any()); var asdd = new AdvisorySituationDataDeposit(); asdd.setTimeToLive(TimeToLive.Day); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act var exceptions = uut.resubmitToOde(activeTimIds); @@ -697,31 +738,33 @@ public void resubmitToOde_SdxExistingFail() { var ex = exceptions.get(0); Assertions.assertEquals(new ResubmitTimException(activeTimId, "exception"), ex); verifyNoInteractions(mockPathNodeXYService); - + verify(mockActiveTimHoldingService).insertActiveTimHolding(any()); verify(mockMilepostService).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, mockRsuService, - mockOdeService, mockActiveTimHoldingService); + verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, + mockRsuService, mockOdeService, mockActiveTimHoldingService); } @Test - public void resubmitToOde_SdxExistingSuccess() { + public void resubmitToOde_SdxExistingSuccess() throws Utility.IdenticalPointsException { // Arrange - List activeTimIds = new ArrayList(); + List activeTimIds = new ArrayList<>(); activeTimIds.add(-1L); setupActiveTimModel(); setupMilepostReturn(); tum.setRoute("I 80"); tum.setSatRecordId("satRecordId"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); doReturn("").when(mockOdeService).updateTimOnSdw(any()); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act var exceptions = uut.resubmitToOde(activeTimIds); @@ -729,11 +772,73 @@ public void resubmitToOde_SdxExistingSuccess() { // Assert Assertions.assertEquals(0, exceptions.size()); verifyNoInteractions(mockPathNodeXYService); + verify(mockActiveTimHoldingService).insertActiveTimHolding(any()); + verify(mockMilepostService).getMilepostsByStartEndPointDirection(any()); + verify(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); + verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, + mockRsuService, mockOdeService, mockActiveTimHoldingService); + } + @Test + public void resubmitToOde_IdenticalPointsException_SuccessfulRecovery() throws Utility.IdenticalPointsException { + // Arrange + List activeTimIds = new ArrayList<>(); + activeTimIds.add(-1L); + setupActiveTimModel(); + setupMilepostReturn(); + tum.setRoute("I 80"); + tum.setSatRecordId("satRecordId"); + + List mps = new ArrayList<>(); + mps.add(new Milepost()); + doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); + + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); + doReturn("").when(mockOdeService).updateTimOnSdw(any()); + + doThrow(new Utility.IdenticalPointsException()).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); + + Milepost anchor = new Milepost(); + doReturn(anchor).when(mockIdenticalPointsExceptionHandler).recover(any()); + + // Act + var exceptions = uut.resubmitToOde(activeTimIds); + + // Assert + Assertions.assertEquals(0, exceptions.size()); + verifyNoInteractions(mockPathNodeXYService); + verify(mockActiveTimHoldingService).insertActiveTimHolding(any()); verify(mockMilepostService).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, mockRsuService, - mockOdeService, mockActiveTimHoldingService); + verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, + mockRsuService, mockOdeService, mockActiveTimHoldingService); + } + + @Test + public void resubmitToOde_IdenticalPointsException_FailureToRecover() throws Utility.IdenticalPointsException { + // Arrange + List activeTimIds = new ArrayList<>(); + activeTimIds.add(-1L); + setupActiveTimModel(); + setupMilepostReturn(); + + doThrow(new Utility.IdenticalPointsException()).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); + + doReturn(null).when(mockIdenticalPointsExceptionHandler).recover(any()); + + // Act + var exceptions = uut.resubmitToOde(activeTimIds); + + // Assert + Assertions.assertEquals(1, exceptions.size()); + var ex = exceptions.get(0); + Assertions.assertEquals(new ResubmitTimException(activeTimId, String.format("Unable to resubmit TIM, identical points found while calculating anchor point for Active_Tim %d", activeTimId)), ex); + verifyNoInteractions(mockPathNodeXYService); + verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, + mockRsuService, mockOdeService, mockActiveTimHoldingService); } @Test @@ -745,9 +850,9 @@ public void updateAndResubmitToOde_nullValidationResults() { // Assert Assertions.assertEquals(0, exceptions.size()); - verifyNoInteractions(mockDataFrameService, mockPathNodeXYService, mockRegionService, mockRsuService, - mockOdeService, mockActiveTimHoldingService, mockSdwService, mockMilepostService, mockMilepostReduction, - mockActiveTimService); + verifyNoInteractions(mockDataFrameService, mockPathNodeXYService, mockRegionService, + mockRsuService, mockOdeService, mockActiveTimHoldingService, mockSdwService, + mockMilepostService, mockMilepostReduction, mockActiveTimService); } @Test @@ -759,9 +864,9 @@ public void updateAndResubmitToOde_emptyValidationResults() { // Assert Assertions.assertEquals(0, exceptions.size()); - verifyNoInteractions(mockDataFrameService, mockPathNodeXYService, mockRegionService, mockRsuService, - mockOdeService, mockActiveTimHoldingService, mockSdwService, mockMilepostService, mockMilepostReduction, - mockActiveTimService); + verifyNoInteractions(mockDataFrameService, mockPathNodeXYService, mockRegionService, + mockRsuService, mockOdeService, mockActiveTimHoldingService, mockSdwService, + mockMilepostService, mockMilepostReduction, mockActiveTimService); } @Test @@ -778,9 +883,9 @@ public void updateAndResubmitToOde_nullTum() { String exMsg = "Failed to get Update Model from active tim"; Assertions.assertEquals(new ResubmitTimException(activeTimId, exMsg), ex); verify(mockActiveTimService).getUpdateModelFromActiveTimId(any()); - verifyNoInteractions(mockDataFrameService, mockPathNodeXYService, mockRegionService, mockRsuService, - mockOdeService, mockActiveTimHoldingService, mockSdwService, mockMilepostService, - mockMilepostReduction); + verifyNoInteractions(mockDataFrameService, mockPathNodeXYService, mockRegionService, + mockRsuService, mockOdeService, mockActiveTimHoldingService, mockSdwService, + mockMilepostService, mockMilepostReduction); } @@ -788,7 +893,7 @@ public void updateAndResubmitToOde_nullTum() { public void updateAndResubmitToOde_noMileposts() { // Arrange setupActiveTimModel(); - List mps = new ArrayList(); + List mps = new ArrayList<>(); doReturn(mps).when(mockMilepostService).getMilepostsByStartEndPointDirection(any()); // Act @@ -798,26 +903,29 @@ public void updateAndResubmitToOde_noMileposts() { Assertions.assertEquals(1, exceptions.size()); var ex = exceptions.get(0); String exMsg = String.format( - "Unable to resubmit TIM, less than 2 mileposts found to determine service area for Active_Tim %d", activeTimId); + "Unable to resubmit TIM, less than 2 mileposts found to determine service area for Active_Tim %d", + activeTimId); Assertions.assertEquals(new ResubmitTimException(activeTimId, exMsg), ex); - verifyNoInteractions(mockDataFrameService, mockPathNodeXYService, mockRegionService, mockRsuService, - mockOdeService, mockActiveTimHoldingService, mockSdwService); + verifyNoInteractions(mockDataFrameService, mockPathNodeXYService, mockRegionService, + mockRsuService, mockOdeService, mockActiveTimHoldingService, mockSdwService); verify(mockMilepostService).getMilepostsByStartEndPointDirection(any()); verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction); } @Test - public void updateAndResubmitToOde_RsuNewTimFail_EndPointMps() { + public void updateAndResubmitToOde_RsuNewTimFail_EndPointMps() + throws Utility.IdenticalPointsException { // Arrange setupActiveTimModel(); setupMilepostReturnSecondFail(); tum.setRoute("I 80"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); var validationResults = getValidationResults(); var errors = new ArrayList(); @@ -826,7 +934,8 @@ public void updateAndResubmitToOde_RsuNewTimFail_EndPointMps() { errors.add(new ActiveTimError(ActiveTimErrorType.endPoint, "timValue", gson.toJson(c))); validationResults.get(0).setErrors(errors); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act var exceptions = uut.updateAndResubmitToOde(validationResults); @@ -835,34 +944,38 @@ public void updateAndResubmitToOde_RsuNewTimFail_EndPointMps() { Assertions.assertEquals(1, exceptions.size()); var ex = exceptions.get(0); String exMsg = String.format( - "Unable to resubmit TIM, less than 2 mileposts found to determine service area for Active_Tim %d", activeTimId); + "Unable to resubmit TIM, less than 2 mileposts found to determine service area for Active_Tim %d", + activeTimId); Assertions.assertEquals(new ResubmitTimException(activeTimId, exMsg), ex); verify(mockDataFrameService).getItisCodesForDataFrameId(any()); verify(mockMilepostService, times(2)).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService); - verifyNoInteractions(mockPathNodeXYService, mockRegionService, mockSdwService, mockOdeService, - mockActiveTimHoldingService, mockRsuService); + verifyNoInteractions(mockPathNodeXYService, mockRegionService, mockSdwService, + mockOdeService, mockActiveTimHoldingService, mockRsuService); } @Test - public void updateAndResubmitToOde_RsuNewTimFail_EndTimeParse() { + public void updateAndResubmitToOde_RsuNewTimFail_EndTimeParse() + throws Utility.IdenticalPointsException { // Arrange setupActiveTimModel(); setupMilepostReturnSecondFail(); tum.setRoute("I 80"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); var validationResults = getValidationResults(); var errors = new ArrayList(); errors.add(new ActiveTimError(ActiveTimErrorType.endTime, "timValue", "badTimeValue")); validationResults.get(0).setErrors(errors); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act var exceptions = uut.updateAndResubmitToOde(validationResults); @@ -870,28 +983,30 @@ public void updateAndResubmitToOde_RsuNewTimFail_EndTimeParse() { // Assert Assertions.assertEquals(1, exceptions.size()); var ex = exceptions.get(0); - String exMsg = String.format("Failed to parse associated FEU date: badTimeValue"); + String exMsg = "Failed to parse associated FEU date: badTimeValue"; Assertions.assertEquals(new ResubmitTimException(activeTimId, exMsg), ex); verify(mockDataFrameService).getItisCodesForDataFrameId(any()); verify(mockMilepostService).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService); - verifyNoInteractions(mockPathNodeXYService, mockRegionService, mockSdwService, mockOdeService, - mockActiveTimHoldingService, mockRsuService); + verifyNoInteractions(mockPathNodeXYService, mockRegionService, mockSdwService, + mockOdeService, mockActiveTimHoldingService, mockRsuService); } @Test - public void updateAndResubmitToOde_RsuNewTimFail_StartPointMps() { + public void updateAndResubmitToOde_RsuNewTimFail_StartPointMps() + throws Utility.IdenticalPointsException { // Arrange setupActiveTimModel(); setupMilepostReturnSecondFail(); tum.setRoute("I 80"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); var validationResults = getValidationResults(); var errors = new ArrayList(); @@ -900,7 +1015,8 @@ public void updateAndResubmitToOde_RsuNewTimFail_StartPointMps() { errors.add(new ActiveTimError(ActiveTimErrorType.startPoint, "timValue", gson.toJson(c))); validationResults.get(0).setErrors(errors); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act var exceptions = uut.updateAndResubmitToOde(validationResults); @@ -909,31 +1025,35 @@ public void updateAndResubmitToOde_RsuNewTimFail_StartPointMps() { Assertions.assertEquals(1, exceptions.size()); var ex = exceptions.get(0); String exMsg = String.format( - "Unable to resubmit TIM, less than 2 mileposts found to determine service area for Active_Tim %d", activeTimId); + "Unable to resubmit TIM, less than 2 mileposts found to determine service area for Active_Tim %d", + activeTimId); Assertions.assertEquals(new ResubmitTimException(activeTimId, exMsg), ex); verify(mockDataFrameService).getItisCodesForDataFrameId(any()); verify(mockMilepostService, times(2)).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService); - verifyNoInteractions(mockPathNodeXYService, mockRegionService, mockSdwService, mockOdeService, - mockActiveTimHoldingService, mockRsuService); + verifyNoInteractions(mockPathNodeXYService, mockRegionService, mockSdwService, + mockOdeService, mockActiveTimHoldingService, mockRsuService); } @Test - public void updateAndResubmitToOde_RsuNewTimSuccess_StartPoint() { + public void updateAndResubmitToOde_RsuNewTimSuccess_StartPoint() + throws Utility.IdenticalPointsException { // Arrange setupActiveTimModel(); setupMilepostReturn(); tum.setRoute("I 80"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - String[] rsuRoutes = new String[] { "I 80" }; + String[] rsuRoutes = new String[] {"I 80"}; doReturn(rsuRoutes).when(mockConfig).getRsuRoutes(); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); var validationResults = getValidationResults(); var errors = new ArrayList(); @@ -947,7 +1067,8 @@ public void updateAndResubmitToOde_RsuNewTimSuccess_StartPoint() { rsu.setRsuTarget("10.10.10.10"); dbRsus.add(rsu); doReturn(dbRsus).when(mockRsuService).getRsusByLatLong(any(), any(), any(), any()); - when(mockOdeService.submitTimQuery(isA(WydotRsu.class), isA(Integer.class))).thenReturn(new TimQuery()); + when(mockOdeService.submitTimQuery(isA(WydotRsu.class), isA(Integer.class))).thenReturn( + new TimQuery()); when(mockOdeService.findFirstAvailableIndexWithRsuIndex(any())).thenReturn(1); // Act @@ -967,23 +1088,25 @@ public void updateAndResubmitToOde_RsuNewTimSuccess_StartPoint() { verify(mockMilepostService, times(2)).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction, times(2)).applyMilepostReductionAlgorithm(any(), any()); - verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, mockRsuService, - mockOdeService, mockActiveTimHoldingService); + verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, + mockRsuService, mockOdeService, mockActiveTimHoldingService); } @Test - public void updateAndResubmitToOde_RsuNewTimSuccess_EndPoint() { + public void updateAndResubmitToOde_RsuNewTimSuccess_EndPoint() + throws Utility.IdenticalPointsException { // Arrange setupActiveTimModel(); setupMilepostReturn(); tum.setRoute("I 80"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - String[] rsuRoutes = new String[] { "I 80" }; + String[] rsuRoutes = new String[] {"I 80"}; doReturn(rsuRoutes).when(mockConfig).getRsuRoutes(); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); var validationResults = getValidationResults(); var errors = new ArrayList(); @@ -997,10 +1120,12 @@ public void updateAndResubmitToOde_RsuNewTimSuccess_EndPoint() { rsu.setRsuTarget("10.10.10.10"); dbRsus.add(rsu); doReturn(dbRsus).when(mockRsuService).getRsusByLatLong(any(), any(), any(), any()); - when(mockOdeService.submitTimQuery(isA(WydotRsu.class), isA(Integer.class))).thenReturn(new TimQuery()); + when(mockOdeService.submitTimQuery(isA(WydotRsu.class), isA(Integer.class))).thenReturn( + new TimQuery()); when(mockOdeService.findFirstAvailableIndexWithRsuIndex(any())).thenReturn(1); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act var exceptions = uut.updateAndResubmitToOde(validationResults); @@ -1019,27 +1144,30 @@ public void updateAndResubmitToOde_RsuNewTimSuccess_EndPoint() { verify(mockMilepostService, times(2)).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction, times(2)).applyMilepostReductionAlgorithm(any(), any()); - verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, mockRsuService, - mockOdeService, mockActiveTimHoldingService); + verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, + mockRsuService, mockOdeService, mockActiveTimHoldingService); } @Test - public void updateAndResubmitToOde_RsuNewTimSuccess_EndTime() { + public void updateAndResubmitToOde_RsuNewTimSuccess_EndTime() + throws Utility.IdenticalPointsException { // Arrange setupActiveTimModel(); setupMilepostReturn(); tum.setRoute("I 80"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - String[] rsuRoutes = new String[] { "I 80" }; + String[] rsuRoutes = new String[] {"I 80"}; doReturn(rsuRoutes).when(mockConfig).getRsuRoutes(); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); var validationResults = getValidationResults(); var errors = new ArrayList(); - errors.add(new ActiveTimError(ActiveTimErrorType.endTime, "timValue", "2020-12-08 09:31:00")); + errors.add( + new ActiveTimError(ActiveTimErrorType.endTime, "timValue", "2020-12-08 09:31:00")); validationResults.get(0).setErrors(errors); List dbRsus = new ArrayList<>(); @@ -1047,10 +1175,12 @@ public void updateAndResubmitToOde_RsuNewTimSuccess_EndTime() { rsu.setRsuTarget("10.10.10.10"); dbRsus.add(rsu); doReturn(dbRsus).when(mockRsuService).getRsusByLatLong(any(), any(), any(), any()); - when(mockOdeService.submitTimQuery(isA(WydotRsu.class), isA(Integer.class))).thenReturn(new TimQuery()); + when(mockOdeService.submitTimQuery(isA(WydotRsu.class), isA(Integer.class))).thenReturn( + new TimQuery()); when(mockOdeService.findFirstAvailableIndexWithRsuIndex(any())).thenReturn(1); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act var exceptions = uut.updateAndResubmitToOde(validationResults); @@ -1069,23 +1199,25 @@ public void updateAndResubmitToOde_RsuNewTimSuccess_EndTime() { verify(mockMilepostService).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, mockRsuService, - mockOdeService, mockActiveTimHoldingService); + verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, + mockRsuService, mockOdeService, mockActiveTimHoldingService); } @Test - public void updateAndResubmitToOde_RsuUpdateTimSuccess_ItisCodes() { + public void updateAndResubmitToOde_RsuUpdateTimSuccess_ItisCodes() + throws Utility.IdenticalPointsException { // Arrange setupActiveTimModel(); setupMilepostReturn(); tum.setRoute("I 80"); - List mps = new ArrayList(); + List mps = new ArrayList<>(); mps.add(new Milepost()); doReturn(mps).when(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - String[] rsuRoutes = new String[] { "I 80" }; + String[] rsuRoutes = new String[] {"I 80"}; doReturn(rsuRoutes).when(mockConfig).getRsuRoutes(); - doReturn(new String[] { "1234" }).when(mockDataFrameService).getItisCodesForDataFrameId(any()); + doReturn(new String[] {"1234"}).when(mockDataFrameService) + .getItisCodesForDataFrameId(any()); var validationResults = getValidationResults(); var errors = new ArrayList(); @@ -1098,7 +1230,8 @@ public void updateAndResubmitToOde_RsuUpdateTimSuccess_ItisCodes() { wydotRsus.add(wydotRsuTim); doReturn(wydotRsus).when(mockRsuService).getFullRsusTimIsOn(any()); - doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility).calculateAnchorCoordinate(any(), any()); + doReturn(new Coordinate(BigDecimal.valueOf(1), BigDecimal.valueOf(2))).when(mockUtility) + .calculateAnchorCoordinate(any(), any()); // Act var exceptions = uut.updateAndResubmitToOde(validationResults); @@ -1113,15 +1246,15 @@ public void updateAndResubmitToOde_RsuUpdateTimSuccess_ItisCodes() { verify(mockMilepostService).getMilepostsByStartEndPointDirection(any()); verify(mockMilepostReduction).applyMilepostReductionAlgorithm(any(), any()); - verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, mockRsuService, - mockOdeService, mockActiveTimHoldingService); + verifyNoMoreInteractions(mockMilepostService, mockMilepostReduction, mockDataFrameService, + mockRsuService, mockOdeService, mockActiveTimHoldingService); } @Test public void isValidTim_TRUE() { // Arrange var tum = new TimUpdateModel(); - tum.setStartPoint(new Coordinate(BigDecimal.valueOf(1l), BigDecimal.valueOf(2l))); + tum.setStartPoint(new Coordinate(BigDecimal.valueOf(1L), BigDecimal.valueOf(2L))); tum.setDirection("I"); tum.setRoute("I 80"); @@ -1150,7 +1283,7 @@ public void isValidTim_FALSE_StartPoint() { public void isValidTim_FALSE_Direction() { // Arrange var tum = new TimUpdateModel(); - tum.setStartPoint(new Coordinate(BigDecimal.valueOf(1l), BigDecimal.valueOf(2l))); + tum.setStartPoint(new Coordinate(BigDecimal.valueOf(1L), BigDecimal.valueOf(2L))); tum.setRoute("I 80"); // Act @@ -1164,7 +1297,7 @@ public void isValidTim_FALSE_Direction() { public void isValidTim_FALSE_Route() { // Arrange var tum = new TimUpdateModel(); - tum.setStartPoint(new Coordinate(BigDecimal.valueOf(1l), BigDecimal.valueOf(2l))); + tum.setStartPoint(new Coordinate(BigDecimal.valueOf(1L), BigDecimal.valueOf(2L))); tum.setDirection("I"); // Act @@ -1174,11 +1307,37 @@ public void isValidTim_FALSE_Route() { Assertions.assertFalse(success); } + @Test + public void getNextMessageCount_ShouldIncrementFrom2To3() { + // Arrange + int currentMsgCnt = 2; + int expectedMsgCnt = 3; + + // Act + var msgCnt = uut.getNextMessageCount(currentMsgCnt); + + // Assert + Assertions.assertEquals(expectedMsgCnt, msgCnt); + } + + @Test + public void getNextMessageCount_ShouldRollOverFrom127To1() { + // Arrange + int currentMsgCnt = 127; + int expectedMsgCnt = 1; + + // Act + var msgCnt = uut.getNextMessageCount(currentMsgCnt); + + // Assert + Assertions.assertEquals(expectedMsgCnt, msgCnt); + } + private void setupActiveTimModel() { tum = new TimUpdateModel(); tum.setActiveTimId(activeTimId); - tum.setStartPoint(new Coordinate(BigDecimal.valueOf(-1l), BigDecimal.valueOf(-2l))); - tum.setEndPoint(new Coordinate(BigDecimal.valueOf(-3l), BigDecimal.valueOf(-4l))); + tum.setStartPoint(new Coordinate(BigDecimal.valueOf(-1L), BigDecimal.valueOf(-2L))); + tum.setEndPoint(new Coordinate(BigDecimal.valueOf(-3L), BigDecimal.valueOf(-4L))); // TIM Props tum.setMsgCnt(1);// int @@ -1194,9 +1353,9 @@ private void setupActiveTimModel() { // Region properties tum.setRegionId(-1);// Integer tum.setRegionDescription("descrip");// String - tum.setLaneWidth(BigDecimal.valueOf(50l));// BigDecimal - tum.setAnchorLat(BigDecimal.valueOf(-1l));// BigDecimal - tum.setAnchorLong(BigDecimal.valueOf(-2l));// BigDecimal + tum.setLaneWidth(BigDecimal.valueOf(50L));// BigDecimal + tum.setAnchorLat(BigDecimal.valueOf(-1L));// BigDecimal + tum.setAnchorLong(BigDecimal.valueOf(-2L));// BigDecimal tum.setRegionDirection("I");// String tum.setClosedPath(false); @@ -1208,8 +1367,8 @@ private void setupActiveTimModel() { private List getAllMps() { List allMps = new ArrayList<>(); - var latitude = BigDecimal.valueOf(-1l); - var longitude = BigDecimal.valueOf(-2l); + var latitude = BigDecimal.valueOf(-1L); + var longitude = BigDecimal.valueOf(-2L); var mp = new Milepost(); mp.setCommonName("I 80"); mp.setDirection("I"); @@ -1232,8 +1391,8 @@ private void setupMilepostReturn() { } private void setupMilepostReturnSecondFail() { - when(mockMilepostService.getMilepostsByStartEndPointDirection(any())).thenReturn(getAllMps()) - .thenReturn(new ArrayList<>()); + when(mockMilepostService.getMilepostsByStartEndPointDirection(any())).thenReturn( + getAllMps()).thenReturn(new ArrayList<>()); } private List getValidationResults() { @@ -1245,7 +1404,7 @@ private List getValidationResults() { private ActiveTim getActiveTim() { var tim = new ActiveTim(); - tim.setActiveTimId(-1l); + tim.setActiveTimId(-1L); return tim; } } \ No newline at end of file diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/UtilityTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/UtilityTest.java index cceaa289f..23ee4ca47 100644 --- a/cv-data-service-library/src/test/java/com/trihydro/library/helpers/UtilityTest.java +++ b/cv-data-service-library/src/test/java/com/trihydro/library/helpers/UtilityTest.java @@ -29,7 +29,7 @@ public void convertDate_min_SUCCESS() { // Assert Assertions.assertNotNull(convertedDate); - Assertions.assertEquals(1603896780000l, convertedDate.getTime()); + Assertions.assertEquals(1603896780000L, convertedDate.getTime()); } @Test @@ -43,7 +43,7 @@ public void convertDate_sec_SUCCESS() { // Assert Assertions.assertNotNull(convertedDate); - Assertions.assertEquals(1603896780000l, convertedDate.getTime()); + Assertions.assertEquals(1603896780000L, convertedDate.getTime()); } @Test @@ -57,7 +57,7 @@ public void convertDate_milli_SUCCESS() { // Assert Assertions.assertNotNull(convertedDate); - Assertions.assertEquals(1603896780123l, convertedDate.getTime()); + Assertions.assertEquals(1603896780123L, convertedDate.getTime()); } @Test @@ -71,7 +71,7 @@ public void convertDate_utcText_SUCCESS() { // Assert Assertions.assertNotNull(convertedDate); - Assertions.assertEquals(1581354000000l, convertedDate.getTime()); + Assertions.assertEquals(1581354000000L, convertedDate.getTime()); } @Test @@ -93,7 +93,7 @@ public void getMinutesDurationBetweenTwoDates_SUCCESS_zonedDate() { // Arrange // "dd-MMM-yy HH.mm.ss" String startDateTime = ZonedDateTime.now(ZoneId.of("UTC")).toString(); - String endDateTime = ZonedDateTime.now(ZoneId.of("UTC")).plusMinutes(10l).toString(); + String endDateTime = ZonedDateTime.now(ZoneId.of("UTC")).plusMinutes(10L).toString(); // Act var duration = uut.getMinutesDurationBetweenTwoDates(startDateTime, endDateTime); @@ -143,98 +143,168 @@ public void getMinutesDurationBetweenTwoDates_FAILURE_unrecognized_format() { } @Test - public void calculateAnchorCoordinateExample1() { + public void calculateAnchorCoordinateExample1() throws Utility.IdenticalPointsException { // Arrange - BigDecimal firstLat = new BigDecimal(-29.944604); - BigDecimal firstLon = new BigDecimal(-71.135100); - BigDecimal secondLat = new BigDecimal(-29.945241); - BigDecimal secondLon = new BigDecimal(-71.134510); - Milepost firstMilepost = new Milepost(); - firstMilepost.setLatitude(firstLat); - firstMilepost.setLongitude(firstLon); - Milepost secondMilepost = new Milepost(); - secondMilepost.setLatitude(secondLat); - secondMilepost.setLongitude(secondLon); + BigDecimal firstLat = BigDecimal.valueOf(-29.944604); + BigDecimal firstLon = BigDecimal.valueOf(-71.135100); + BigDecimal secondLat = BigDecimal.valueOf(-29.945241); + BigDecimal secondLon = BigDecimal.valueOf(-71.134510); + Milepost firstMilepost = new Milepost(firstLat, firstLon); + Milepost secondMilepost = new Milepost(secondLat, secondLon); // Act Coordinate result = uut.calculateAnchorCoordinate(firstMilepost, secondMilepost); - + // Assert - BigDecimal expectedLat = new BigDecimal(-29.944498794493846).round(new java.math.MathContext(8)); - BigDecimal expectedLon = new BigDecimal(-71.13519744309046).round(new java.math.MathContext(9)); - Assertions.assertEquals(expectedLat, result.getLatitude().round(new java.math.MathContext(8))); - Assertions.assertEquals(expectedLon, result.getLongitude().round(new java.math.MathContext(9))); + BigDecimal expectedLat = + BigDecimal.valueOf(-29.944498794493846).round(new java.math.MathContext(8)); + BigDecimal expectedLon = + BigDecimal.valueOf(-71.13519744309046).round(new java.math.MathContext(9)); + Assertions.assertEquals(expectedLat, + result.getLatitude().round(new java.math.MathContext(8))); + Assertions.assertEquals(expectedLon, + result.getLongitude().round(new java.math.MathContext(9))); } @Test - public void calculateAnchorCoordinateExample2() { + public void calculateAnchorCoordinateExample2() throws Utility.IdenticalPointsException { // Arrange - BigDecimal firstLat = new BigDecimal(-34.864022); - BigDecimal firstLon = new BigDecimal(138.783680); - BigDecimal secondLat = new BigDecimal(-34.864070); - BigDecimal secondLon = new BigDecimal(138.779918); - Milepost firstMilepost = new Milepost(); - firstMilepost.setLatitude(firstLat); - firstMilepost.setLongitude(firstLon); - Milepost secondMilepost = new Milepost(); - secondMilepost.setLatitude(secondLat); - secondMilepost.setLongitude(secondLon); + BigDecimal firstLat = BigDecimal.valueOf(-34.864022); + BigDecimal firstLon = BigDecimal.valueOf(138.783680); + BigDecimal secondLat = BigDecimal.valueOf(-34.864070); + BigDecimal secondLon = BigDecimal.valueOf(138.779918); + Milepost firstMilepost = new Milepost(firstLat, firstLon); + Milepost secondMilepost = new Milepost(secondLat, secondLon); // Act Coordinate result = uut.calculateAnchorCoordinate(firstMilepost, secondMilepost); - + // Assert - BigDecimal expectedLat = new BigDecimal(-34.864019902550346).round(new java.math.MathContext(8)); - BigDecimal expectedLon = new BigDecimal(138.78384438761637).round(new java.math.MathContext(9)); - Assertions.assertEquals(expectedLat, result.getLatitude().round(new java.math.MathContext(8))); - Assertions.assertEquals(expectedLon, result.getLongitude().round(new java.math.MathContext(9))); + BigDecimal expectedLat = + BigDecimal.valueOf(-34.864019902550346).round(new java.math.MathContext(8)); + BigDecimal expectedLon = + BigDecimal.valueOf(138.78384438761637).round(new java.math.MathContext(9)); + Assertions.assertEquals(expectedLat, + result.getLatitude().round(new java.math.MathContext(8))); + Assertions.assertEquals(expectedLon, + result.getLongitude().round(new java.math.MathContext(9))); } @Test - public void calculateAnchorCoordinateExample3() { + public void calculateAnchorCoordinateExample3() throws Utility.IdenticalPointsException { // Arrange - BigDecimal firstLat = new BigDecimal(36.879930); - BigDecimal firstLon = new BigDecimal(139.924244); - BigDecimal secondLat = new BigDecimal(36.874820); - BigDecimal secondLon = new BigDecimal(139.918023); - Milepost firstMilepost = new Milepost(); - firstMilepost.setLatitude(firstLat); - firstMilepost.setLongitude(firstLon); - Milepost secondMilepost = new Milepost(); - secondMilepost.setLatitude(secondLat); - secondMilepost.setLongitude(secondLon); + BigDecimal firstLat = BigDecimal.valueOf(36.879930); + BigDecimal firstLon = BigDecimal.valueOf(139.924244); + BigDecimal secondLat = BigDecimal.valueOf(36.874820); + BigDecimal secondLon = BigDecimal.valueOf(139.918023); + Milepost firstMilepost = new Milepost(firstLat, firstLon); + Milepost secondMilepost = new Milepost(secondLat, secondLon); // Act Coordinate result = uut.calculateAnchorCoordinate(firstMilepost, secondMilepost); - + // Assert - BigDecimal expectedLat = new BigDecimal(36.88002664477771).round(new java.math.MathContext(8)); - BigDecimal expectedLon = new BigDecimal(139.92436165697887).round(new java.math.MathContext(9)); - Assertions.assertEquals(expectedLat, result.getLatitude().round(new java.math.MathContext(8))); - Assertions.assertEquals(expectedLon, result.getLongitude().round(new java.math.MathContext(9))); + BigDecimal expectedLat = + BigDecimal.valueOf(36.88002664477771).round(new java.math.MathContext(8)); + BigDecimal expectedLon = + BigDecimal.valueOf(139.92436165697887).round(new java.math.MathContext(9)); + Assertions.assertEquals(expectedLat, + result.getLatitude().round(new java.math.MathContext(8))); + Assertions.assertEquals(expectedLon, + result.getLongitude().round(new java.math.MathContext(9))); } @Test - public void calculateAnchorCoordinateExample4() { + public void calculateAnchorCoordinateExample4() throws Utility.IdenticalPointsException { // Arrange - BigDecimal firstLat = new BigDecimal(39.503475); - BigDecimal firstLon = new BigDecimal(-106.147423); - BigDecimal secondLat = new BigDecimal(39.50418); - BigDecimal secondLon = new BigDecimal(-106.145944); - Milepost firstMilepost = new Milepost(); - firstMilepost.setLatitude(firstLat); - firstMilepost.setLongitude(firstLon); - Milepost secondMilepost = new Milepost(); - secondMilepost.setLatitude(secondLat); - secondMilepost.setLongitude(secondLon); + BigDecimal firstLat = BigDecimal.valueOf(39.503475); + BigDecimal firstLon = BigDecimal.valueOf(-106.147423); + BigDecimal secondLat = BigDecimal.valueOf(39.50418); + BigDecimal secondLon = BigDecimal.valueOf(-106.145944); + Milepost firstMilepost = new Milepost(firstLat, firstLon); + Milepost secondMilepost = new Milepost(secondLat, secondLon); // Act Coordinate result = uut.calculateAnchorCoordinate(firstMilepost, secondMilepost); - + // Assert - BigDecimal expectedLat = new BigDecimal(39.503404).round(new java.math.MathContext(8)); - BigDecimal expectedLon = new BigDecimal(-106.147572).round(new java.math.MathContext(9)); - Assertions.assertEquals(expectedLat, result.getLatitude().round(new java.math.MathContext(8))); - Assertions.assertEquals(expectedLon, result.getLongitude().round(new java.math.MathContext(9))); + BigDecimal expectedLat = BigDecimal.valueOf(39.503404).round(new java.math.MathContext(8)); + BigDecimal expectedLon = + BigDecimal.valueOf(-106.147572).round(new java.math.MathContext(9)); + Assertions.assertEquals(expectedLat, + result.getLatitude().round(new java.math.MathContext(8))); + Assertions.assertEquals(expectedLon, + result.getLongitude().round(new java.math.MathContext(9))); } + + @Test + public void calculateAnchorCoordinate_identicalPoints_zero() { + // Arrange + BigDecimal lat = BigDecimal.valueOf(0); + BigDecimal lon = BigDecimal.valueOf(0); + Milepost firstMilepost = new Milepost(lat, lon); + Milepost secondMilepost = new Milepost(lat, lon); + + // Act & Assert + Assertions.assertThrows(Utility.IdenticalPointsException.class, () -> { + uut.calculateAnchorCoordinate(firstMilepost, secondMilepost); + }); + } + + @Test + public void calculateAnchorCoordinate_identicalPoints_positiveLat_negativeLon() { + // Arrange + BigDecimal lat = BigDecimal.valueOf(40); + BigDecimal lon = BigDecimal.valueOf(-100); + Milepost firstMilepost = new Milepost(lat, lon); + Milepost secondMilepost = new Milepost(lat, lon); + + // Act & Assert + Assertions.assertThrows(Utility.IdenticalPointsException.class, () -> { + uut.calculateAnchorCoordinate(firstMilepost, secondMilepost); + }); + } + + @Test + public void calculateAnchorCoordinate_identicalPoints_negativeLat_positiveLon() { + // Arrange + BigDecimal lat = BigDecimal.valueOf(-40); + BigDecimal lon = BigDecimal.valueOf(100); + Milepost firstMilepost = new Milepost(lat, lon); + Milepost secondMilepost = new Milepost(lat, lon); + + // Act & Assert + Assertions.assertThrows(Utility.IdenticalPointsException.class, () -> { + uut.calculateAnchorCoordinate(firstMilepost, secondMilepost); + }); + } + + @Test + public void calculateAnchorCoordinate_identicalPoints_positiveLat_positiveLon() { + // Arrange + BigDecimal lat = BigDecimal.valueOf(-40); + BigDecimal lon = BigDecimal.valueOf(100); + Milepost firstMilepost = new Milepost(lat, lon); + Milepost secondMilepost = new Milepost(lat, lon); + + // Act & Assert + Assertions.assertThrows(Utility.IdenticalPointsException.class, () -> { + uut.calculateAnchorCoordinate(firstMilepost, secondMilepost); + }); + } + + @Test + public void calculateAnchorCoordinate_identicalPoints_negativeLat_negativeLon() { + // Arrange + BigDecimal lat = BigDecimal.valueOf(-40); + BigDecimal lon = BigDecimal.valueOf(-100); + Milepost firstMilepost = new Milepost(lat, lon); + Milepost secondMilepost = new Milepost(lat, lon); + + // Act & Assert + Assertions.assertThrows(Utility.IdenticalPointsException.class, () -> { + uut.calculateAnchorCoordinate(firstMilepost, secondMilepost); + }); + } + } \ No newline at end of file diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/service/ActiveTimHoldingServiceTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/service/ActiveTimHoldingServiceTest.java new file mode 100644 index 000000000..9eafb16e3 --- /dev/null +++ b/cv-data-service-library/src/test/java/com/trihydro/library/service/ActiveTimHoldingServiceTest.java @@ -0,0 +1,110 @@ +package com.trihydro.library.service; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; + +import com.trihydro.library.model.ActiveTimHolding; +import com.trihydro.library.model.ActiveTimHoldingDeleteModel; +import com.trihydro.library.model.CVRestServiceProps; +import java.sql.SQLException; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; + +class ActiveTimHoldingServiceTest extends BaseServiceTest { + + @Mock + private CVRestServiceProps mockConfig; + + @InjectMocks + private ActiveTimHoldingService uut; + + private final String baseUrl = "baseUrl"; + + @BeforeEach + public void setupSubTest() throws SQLException { + doReturn(baseUrl).when(mockConfig).getCvRestService(); + } + + @Test + void getAllRecords_SuccessfulRetrieval_ShouldReturnRecords() { + // Arrange + ActiveTimHolding[] mockRecordsArray = new ActiveTimHolding[1]; + ActiveTimHolding mockRecord = new ActiveTimHolding(); + mockRecordsArray[0] = mockRecord; + ResponseEntity mockResponse = ResponseEntity.ok(mockRecordsArray); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity requestEntity = new HttpEntity<>(headers); + when(mockRestTemplate.exchange(baseUrl + "/active-tim-holding/get-all", HttpMethod.GET, requestEntity, ActiveTimHolding[].class)).thenReturn(mockResponse); + + // Act + List result = uut.getAllRecords(); + + // Assert + assertNotNull(result); + assertEquals(1, result.size()); + } + + @Test + void deleteActiveTimHoldingRecords_SuccessfulDelete_ShouldReturnTrue() { + // Arrange + Long mockId1 = 1L; + Long mockId2 = 2L; + List mockIds = List.of(mockId1, mockId2); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity<>(new ActiveTimHoldingDeleteModel(mockIds), headers); + ResponseEntity mockResponse = ResponseEntity.ok(true); + when(mockRestTemplate.exchange(baseUrl + "/active-tim-holding/delete", HttpMethod.DELETE, entity, Boolean.class)).thenReturn(mockResponse); + + // Act + boolean result = uut.deleteActiveTimHoldingRecords(mockIds); + + // Assert + assertTrue(result); + } + + @Test + void deleteActiveTimHoldingRecords_FailedDelete_ShouldReturnFalse() { + // Arrange + Long mockId1 = 1L; + Long mockId2 = 2L; + List mockIds = List.of(mockId1, mockId2); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity<>(new ActiveTimHoldingDeleteModel(mockIds), headers); + ResponseEntity mockResponse = ResponseEntity.ok(false); + when(mockRestTemplate.exchange(baseUrl + "/active-tim-holding/delete", HttpMethod.DELETE, entity, Boolean.class)).thenReturn(mockResponse); + + // Act + boolean result = uut.deleteActiveTimHoldingRecords(mockIds); + + // Assert + assertFalse(result); + } + + @Test + void deleteActiveTimHoldingRecords_WhenDatabaseConnectionFails_ShouldThrowException() { + // Arrange + Long mockId1 = 1L; + Long mockId2 = 2L; + List mockIds = List.of(mockId1, mockId2); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity<>(new ActiveTimHoldingDeleteModel(mockIds), headers); + when(mockRestTemplate.exchange(baseUrl + "/active-tim-holding/delete/1,2", HttpMethod.DELETE, entity, Boolean.class)).thenThrow( + new RuntimeException("Database error")); + + // Act & Assert + assertThrows(RuntimeException.class, () -> uut.deleteActiveTimHoldingRecords(mockIds)); + } +} \ No newline at end of file diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/service/ActiveTimServiceTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/service/ActiveTimServiceTest.java index d7345017f..af222d08b 100644 --- a/cv-data-service-library/src/test/java/com/trihydro/library/service/ActiveTimServiceTest.java +++ b/cv-data-service-library/src/test/java/com/trihydro/library/service/ActiveTimServiceTest.java @@ -2,7 +2,9 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -115,8 +117,7 @@ public void updateActiveTim_SatRecordId() { String satRecordId = "asdf"; HttpEntity entity = getEntity(null, String.class); String url = String.format("%s/active-tim/update-sat-record-id/%d/%s", baseUrl, activeTimId, satRecordId); - when(mockRestTemplate.exchange(url, HttpMethod.PUT, entity, Boolean.class)) - .thenReturn(mockResponseEntityBoolean); + when(mockRestTemplate.exchange(url, HttpMethod.PUT, entity, Boolean.class)).thenReturn(mockResponseEntityBoolean); // Act Boolean data = uut.updateActiveTim_SatRecordId(activeTimId, satRecordId); @@ -153,8 +154,7 @@ public void deleteActiveTim() { Long activeTimId = -1l; String url = String.format("%s/active-tim/delete-id/%d", baseUrl, activeTimId); HttpEntity entity = getEntity(null, String.class); - when(mockRestTemplate.exchange(url, HttpMethod.DELETE, entity, Boolean.class)) - .thenReturn(mockResponseEntityBoolean); + when(mockRestTemplate.exchange(url, HttpMethod.DELETE, entity, Boolean.class)).thenReturn(mockResponseEntityBoolean); // Act boolean data = uut.deleteActiveTim(activeTimId); @@ -172,8 +172,7 @@ public void deleteActiveTimsById() throws SQLException { activeTimIds.add(-1l); activeTimIds.add(-2l); HttpEntity> entity = new HttpEntity>(activeTimIds, getDefaultHeaders()); - when(mockRestTemplate.exchange(baseUrl + "/active-tim/delete-ids", HttpMethod.DELETE, entity, Boolean.class)) - .thenReturn(mockResponseEntityBoolean); + when(mockRestTemplate.exchange(baseUrl + "/active-tim/delete-ids", HttpMethod.DELETE, entity, Boolean.class)).thenReturn(mockResponseEntityBoolean); // Act boolean success = uut.deleteActiveTimsById(activeTimIds); @@ -207,8 +206,7 @@ public void getActiveTimsByWydotTim() throws SQLException { setupWydotTims(); setupActiveTimArrayReturn(); HttpEntity> entity = new HttpEntity>(wydotTims, getDefaultHeaders()); - when(mockRestTemplate.exchange(baseUrl + "/active-tim/get-by-wydot-tim/" + timTypeId, HttpMethod.POST, entity, - ActiveTim[].class)).thenReturn(mockResponseEntityActiveTims); + when(mockRestTemplate.exchange(baseUrl + "/active-tim/get-by-wydot-tim/" + timTypeId, HttpMethod.POST, entity, ActiveTim[].class)).thenReturn(mockResponseEntityActiveTims); // Act List data = uut.getActiveTimsByWydotTim(wydotTims, timTypeId); @@ -225,8 +223,7 @@ public void getActiveTimsByClientIdDirection() { setupActiveTimArrayReturn(); String clientId = "clientId"; String direction = "westward"; - String url = String.format("%s/active-tim/client-id-direction/%s/%d/%s", baseUrl, clientId, timTypeId, - direction); + String url = String.format("%s/active-tim/client-id-direction/%s/%d/%s", baseUrl, clientId, timTypeId, direction); when(mockRestTemplate.getForEntity(url, ActiveTim[].class)).thenReturn(mockResponseEntityActiveTims); // Act @@ -257,11 +254,11 @@ public void getBufferTimsByClientId() { public void getExpiredActiveTims() { // Arrange setupActiveTimArrayReturn(); - String url = String.format("%s/active-tim/expired", baseUrl); + String url = String.format("%s/active-tim/expired?limit=500", baseUrl); when(mockRestTemplate.getForEntity(url, ActiveTim[].class)).thenReturn(mockResponseEntityActiveTims); // Act - List data = uut.getExpiredActiveTims(); + List data = uut.getExpiredActiveTims(500); // Assert verify(mockRestTemplate).getForEntity(url, ActiveTim[].class); @@ -294,8 +291,7 @@ public void getActiveRsuTim() { String url = String.format("%s/active-tim/active-rsu-tim", baseUrl); ActiveRsuTimQueryModel artqm = new ActiveRsuTimQueryModel(direction, clientId, ipv4Address); HttpEntity entity = getEntity(artqm, ActiveRsuTimQueryModel.class); - when(mockRestTemplate.exchange(url, HttpMethod.POST, entity, ActiveTim.class)) - .thenReturn(mockResponseEntityActiveTim); + when(mockRestTemplate.exchange(url, HttpMethod.POST, entity, ActiveTim.class)).thenReturn(mockResponseEntityActiveTim); // Act ActiveTim data = uut.getActiveRsuTim(artqm); @@ -310,8 +306,7 @@ public void getExpiringActiveTims() { // Arrange setupTUMReturn(); String url = String.format("%s/active-tim/expiring", baseUrl); - when(mockRestTemplate.getForEntity(url, TimUpdateModel[].class)) - .thenReturn(mockResponseEntityTimUpdateModelArray); + when(mockRestTemplate.getForEntity(url, TimUpdateModel[].class)).thenReturn(mockResponseEntityTimUpdateModelArray); // Act List data = uut.getExpiringActiveTims(); @@ -335,8 +330,7 @@ public void getActiveTimsMissingItisCodes() throws SQLException { tum.setActiveTimId(1l); tums[0] = tum; - when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/missing-itis", TimUpdateModel[].class)) - .thenReturn(mockResponseEntity); + when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/missing-itis", TimUpdateModel[].class)).thenReturn(mockResponseEntity); when(mockResponseEntity.getBody()).thenReturn(tums); // Act List ats = uut.getActiveTimsMissingItisCodes(); @@ -359,8 +353,7 @@ public void getActiveTimsNotSent() { tum.setSatRecordId("HEX"); tum.setActiveTimId(1l); tums[0] = tum; - when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/not-sent", TimUpdateModel[].class)) - .thenReturn(mockResponseEntity); + when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/not-sent", TimUpdateModel[].class)).thenReturn(mockResponseEntity); when(mockResponseEntity.getBody()).thenReturn(tums); // Act @@ -376,8 +369,7 @@ public void getActiveTimsNotSent() { public void getActiveTimsForSDX_success() { // Arrange setupActiveTimArrayReturn(); - when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/all-sdx", ActiveTim[].class)) - .thenReturn(mockResponseEntityActiveTims); + when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/all-sdx", ActiveTim[].class)).thenReturn(mockResponseEntityActiveTims); // Act List result = uut.getActiveTimsForSDX(); @@ -390,8 +382,7 @@ public void getActiveTimsForSDX_success() { @Test public void getActiveTimsForSDX_throwsError() { // Arrange - when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/all-sdx", ActiveTim[].class)) - .thenThrow(new RestClientException("timeout")); + when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/all-sdx", ActiveTim[].class)).thenThrow(new RestClientException("timeout")); // Act Assertions.assertThrows(RestClientException.class, () -> { @@ -403,8 +394,7 @@ public void getActiveTimsForSDX_throwsError() { public void getActiveTimsWithItisCodesWithExclusions_success() { // Arrange setupActiveTimArrayReturn(); - when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/all-with-itis?excludeVslAndParking=true", - ActiveTim[].class)).thenReturn(mockResponseEntityActiveTims); + when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/all-with-itis?excludeVslAndParking=true", ActiveTim[].class)).thenReturn(mockResponseEntityActiveTims); // Act List result = uut.getActiveTimsWithItisCodes(true); @@ -418,8 +408,7 @@ public void getActiveTimsWithItisCodesWithExclusions_success() { public void getActiveTimsWithItisCodes_success() { // Arrange setupActiveTimArrayReturn(); - when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/all-with-itis?excludeVslAndParking=false", - ActiveTim[].class)).thenReturn(mockResponseEntityActiveTims); + when(mockRestTemplate.getForEntity(baseUrl + "/active-tim/all-with-itis?excludeVslAndParking=false", ActiveTim[].class)).thenReturn(mockResponseEntityActiveTims); // Act List result = uut.getActiveTimsWithItisCodes(false); @@ -432,8 +421,7 @@ public void getActiveTimsWithItisCodes_success() { @Test public void getActiveTimsWithItisCodes_throwsError() { // Arrange - when(mockRestTemplate.getForEntity(anyString(), eq(ActiveTim[].class))) - .thenThrow(new RestClientException("timeout")); + when(mockRestTemplate.getForEntity(anyString(), eq(ActiveTim[].class))).thenThrow(new RestClientException("timeout")); // Act Assertions.assertThrows(RestClientException.class, () -> { @@ -449,8 +437,7 @@ public void updateActiveTimExpiration_SUCCESS() { String expDate = "2020-10-20T16:26:07.000Z"; HttpEntity entity = getEntity(null, String.class); String url = String.format("%s/active-tim/update-expiration/%s/%s", baseUrl, packetID, expDate); - when(mockRestTemplate.exchange(url, HttpMethod.PUT, entity, Boolean.class)) - .thenReturn(mockResponseEntityBoolean); + when(mockRestTemplate.exchange(url, HttpMethod.PUT, entity, Boolean.class)).thenReturn(mockResponseEntityBoolean); // Act Boolean data = uut.updateActiveTimExpiration(packetID, expDate); @@ -468,8 +455,7 @@ public void updateActiveTimExpiration_FAIL() { String expDate = "2020-10-20T16:26:07.000Z"; HttpEntity entity = getEntity(null, String.class); String url = String.format("%s/active-tim/update-expiration/%s/%s", baseUrl, packetID, expDate); - when(mockRestTemplate.exchange(url, HttpMethod.PUT, entity, Boolean.class)) - .thenReturn(mockResponseEntityBoolean); + when(mockRestTemplate.exchange(url, HttpMethod.PUT, entity, Boolean.class)).thenReturn(mockResponseEntityBoolean); // Act Boolean data = uut.updateActiveTimExpiration(packetID, expDate); @@ -496,4 +482,21 @@ public void getMinExpiration_SUCCESS() { verify(mockRestTemplate).getForEntity(url, String.class); Assertions.assertEquals(minDate, data); } + + @Test + void getAllRecords_SuccessfulRetrieval_ShouldReturnRecords() { + // prepare + ActiveTim[] mockActiveTims = new ActiveTim[1]; + ActiveTim mockActiveTim = new ActiveTim(); + mockActiveTims[0] = mockActiveTim; + ResponseEntity mockResponseEntity = mock(ResponseEntity.class); + when(mockResponseEntity.getBody()).thenReturn(mockActiveTims); + when(mockRestTemplate.exchange(anyString(), any(HttpMethod.class), any(HttpEntity.class), eq(ActiveTim[].class))).thenReturn(mockResponseEntity); + + // execute + List records = uut.getAllRecords(); + + // verify + Assertions.assertEquals(mockActiveTims[0], records.get(0)); + } } \ No newline at end of file diff --git a/cv-data-service-library/src/test/java/com/trihydro/library/service/WydotTimServiceTest.java b/cv-data-service-library/src/test/java/com/trihydro/library/service/WydotTimServiceTest.java index 1c658cf52..e14436a2f 100644 --- a/cv-data-service-library/src/test/java/com/trihydro/library/service/WydotTimServiceTest.java +++ b/cv-data-service-library/src/test/java/com/trihydro/library/service/WydotTimServiceTest.java @@ -15,7 +15,11 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.io.IOException; import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -142,14 +146,14 @@ private List getActiveTims(boolean isSat) { return activeTims; } - private OdeTravelerInformationMessage getMockOdeTravelerInformationMessage() { - String timJson = "{\"msgCnt\":\"1\",\"timeStamp\":\"2017-08-03T22:25:36.297Z\",\"urlB\":\"null\",\"packetID\":\"EC9C236B0000000000\",\"dataframes\":[{\"startDateTime\":\"2017-08-02T22:25:00.000Z\",\"durationTime\":1,\"sspTimRights\":\"0\",\"frameType\":\"advisory\",\"msgId\":{\"roadSignID\":{\"position\":{\"latitude\":\"41.678473\",\"longitude\":\"-108.782775\",\"elevation\":\"917.1432\"},\"viewAngle\":\"1010101010101010\",\"mutcdCode\":\"warning\",\"crc\":\"0000\"}},\"priority\":\"0\",\"sspLocationRights\":\"3\",\"regions\":[{\"name\":\"Testing TIM\",\"regulatorID\":\"0\",\"segmentID\":\"33\",\"anchorPosition\":{\"latitude\":\"41.2500807\",\"longitude\":\"-111.0093847\",\"elevation\":\"2020.6969900289998\"},\"laneWidth\":\"7\",\"directionality\":\"3\",\"closedPath\":\"false\",\"description\":\"path\",\"path\":{\"scale\":\"0\",\"type\":\"ll\",\"nodes\":[{\"nodeLong\":\"0.0030982\",\"nodeLat\":\"0.0014562\",\"delta\":\"node-LL3\"},{\"nodeLong\":\"-111.0093847\",\"nodeLat\":\"41.2500807\",\"delta\":\"node-LatLon\"}]},\"direction\":\"0000000000001010\"}],\"sspMsgTypes\":\"2\",\"sspMsgContent\":\"3\",\"content\":\"Advisory\",\"items\":[\"125\",\"some text\",\"250\",\"'98765\"],\"url\":\"null\"}]}"; + private OdeTravelerInformationMessage getMockOdeTravelerInformationMessage() throws IOException { + String timJson = + new String(Files.readAllBytes(Paths.get("src/test/resources/com/trihydro/library/service/mockOdeTravelerInformationMessage.json"))); Gson gson = new Gson(); - OdeTravelerInformationMessage mockOdeTravelerInformationMessage = gson.fromJson(timJson, OdeTravelerInformationMessage.class); - return mockOdeTravelerInformationMessage; + return gson.fromJson(timJson, OdeTravelerInformationMessage.class); } - private WydotOdeTravelerInformationMessage getMockWydotOdeTravelerInformationMessage() { + private WydotOdeTravelerInformationMessage getMockWydotOdeTravelerInformationMessage() throws IOException { OdeTravelerInformationMessage mockOdeTravelerInformationMessage = getMockOdeTravelerInformationMessage(); WydotOdeTravelerInformationMessage mockWydotOdeTravelerInformationMessage = new WydotOdeTravelerInformationMessage(); mockWydotOdeTravelerInformationMessage.setMsgCnt(mockOdeTravelerInformationMessage.getMsgCnt()); @@ -191,7 +195,7 @@ private WydotTim getMockWydotTim() { return wydotTim; } - private WydotTravelerInputData getMockWydotTravelerInputDataWithServiceRequest() { + private WydotTravelerInputData getMockWydotTravelerInputDataWithServiceRequest() throws IOException { WydotTravelerInputData wydotTravelerInputData = new WydotTravelerInputData(); wydotTravelerInputData.setTim(getMockOdeTravelerInformationMessage()); ServiceRequest serviceRequest = new ServiceRequest(); @@ -379,7 +383,7 @@ public void getAllMilepostsForTim_EndPointNull() { } @Test - public void sendTimToSDW_MultipleActiveSatTimsFound() { + public void sendTimToSDW_MultipleActiveSatTimsFound() throws IOException { // Arrange List activeSatTims = getActiveTims(true); when(mockActiveTimService.getActiveTimsByClientIdDirection(any(), any(), any())).thenReturn(activeSatTims); @@ -403,7 +407,7 @@ public void sendTimToSDW_MultipleActiveSatTimsFound() { } @Test - public void sendTimToSDW_NoActiveSatTimsFound() { + public void sendTimToSDW_NoActiveSatTimsFound() throws IOException { // Arrange when(mockActiveTimService.getActiveTimsByClientIdDirection(any(), any(), any())).thenReturn(new ArrayList<>()); WydotTim wydotTim = getMockWydotTim(); @@ -424,7 +428,7 @@ public void sendTimToSDW_NoActiveSatTimsFound() { } @Test - public void sendTimToSDW_SingleActiveSatTimFound() { + public void sendTimToSDW_SingleActiveSatTimFound() throws IOException { // Arrange List activeSatTims = getActiveTims(true).subList(0, 1); when(mockActiveTimService.getActiveTimsByClientIdDirection(any(), any(), any())).thenReturn(activeSatTims); @@ -448,7 +452,7 @@ public void sendTimToSDW_SingleActiveSatTimFound() { } @Test - public void sendTimToRsus_NoRsusFound() { + public void sendTimToRsus_NoRsusFound() throws IOException { // Arrange when(mockRsuService.getRsusByLatLong(any(), any(), any(), any())).thenReturn(new ArrayList<>()); @@ -468,7 +472,7 @@ public void sendTimToRsus_NoRsusFound() { } @Test - public void sendTimToRsus_RsusFound_NoActiveTIMs() { + public void sendTimToRsus_RsusFound_NoActiveTIMs() throws IOException { // Arrange List rsus = new ArrayList<>(); WydotRsu rsu = new WydotRsu(); @@ -499,7 +503,7 @@ public void sendTimToRsus_RsusFound_NoActiveTIMs() { } @Test - public void sendTimToRsus_RsusFound_ActiveTIMsExist() { + public void sendTimToRsus_RsusFound_ActiveTIMsExist() throws IOException { // Arrange List rsus = new ArrayList<>(); WydotRsu rsu = new WydotRsu(); diff --git a/cv-data-service-library/src/test/resources/broadcastTim_OdeOutput.json b/cv-data-service-library/src/test/resources/broadcastTim_OdeOutput.json index 2889ddd83..6558c2134 100644 --- a/cv-data-service-library/src/test/resources/broadcastTim_OdeOutput.json +++ b/cv-data-service-library/src/test/resources/broadcastTim_OdeOutput.json @@ -22,7 +22,7 @@ "urlB": "null", "dataframes": [ { - "notUsed": 1, + "doNotUse1": 0, "frameType": "advisory", "msgId": { "furtherInfoID": "CDEF" @@ -30,7 +30,7 @@ "startDateTime": "2018-03-15T21:18:46.719-07:00", "durationTime": 32000, "priority": 5, - "notUsed1": 1, + "doNotUse2": 0, "regions": [ { "name": "westbound_I80_370.0_365.0_RSU-10.145.1.100_VSL", @@ -179,8 +179,8 @@ } } ], - "notUsed3": 0, - "notUsed2": 1, + "doNotUse4": 0, + "doNotUse3": 0, "content": "Advisory", "items": [ "268", diff --git a/cv-data-service-library/src/test/resources/com/trihydro/library/service/mockOdeTravelerInformationMessage.json b/cv-data-service-library/src/test/resources/com/trihydro/library/service/mockOdeTravelerInformationMessage.json new file mode 100644 index 000000000..828c86c39 --- /dev/null +++ b/cv-data-service-library/src/test/resources/com/trihydro/library/service/mockOdeTravelerInformationMessage.json @@ -0,0 +1,71 @@ +{ + "msgCnt": "1", + "timeStamp": "2017-08-03T22:25:36.297Z", + "urlB": "null", + "packetID": "EC9C236B0000000000", + "dataframes": [ + { + "startDateTime": "2017-08-02T22:25:00.000Z", + "durationTime": 1, + "doNotUse1": "0", + "frameType": "advisory", + "msgId": { + "roadSignID": { + "position": { + "latitude": "41.678473", + "longitude": "-108.782775", + "elevation": "917.1432" + }, + "viewAngle": "1010101010101010", + "mutcdCode": "warning", + "crc": "0000" + } + }, + "priority": "0", + "doNotUse2": "3", + "regions": [ + { + "name": "Testing TIM", + "regulatorID": "0", + "segmentID": "33", + "anchorPosition": { + "latitude": "41.2500807", + "longitude": "-111.0093847", + "elevation": "2020.6969900289998" + }, + "laneWidth": "7", + "directionality": "3", + "closedPath": "false", + "description": "path", + "path": { + "scale": "0", + "type": "ll", + "nodes": [ + { + "nodeLong": "0.0030982", + "nodeLat": "0.0014562", + "delta": "node-LL3" + }, + { + "nodeLong": "-111.0093847", + "nodeLat": "41.2500807", + "delta": "node-LatLon" + } + ] + }, + "direction": "0000000000001010" + } + ], + "doNotUse4": "2", + "doNotUse3": "3", + "content": "Advisory", + "items": [ + "125", + "some text", + "250", + "'98765" + ], + "url": "null" + } + ] +} \ No newline at end of file diff --git a/cv-data-service-library/src/test/resources/rxMsg_TIM_OdeOutput.json b/cv-data-service-library/src/test/resources/rxMsg_TIM_OdeOutput.json index f61c08d40..169a60727 100644 --- a/cv-data-service-library/src/test/resources/rxMsg_TIM_OdeOutput.json +++ b/cv-data-service-library/src/test/resources/rxMsg_TIM_OdeOutput.json @@ -82,10 +82,10 @@ } }, "durationTime": 32000, - "notUsed2": 0, - "notUsed3": 0, - "notUsed": 0, - "notUsed1": 0, + "doNotUse3": 0, + "doNotUse4": 0, + "doNotUse1": 0, + "doNotUse2": 0, "frameType": { "roadSignage": "" }, diff --git a/cv-data-service-library/src/test/resources/rxMsg_TIM_OdeOutput_Geometry.json b/cv-data-service-library/src/test/resources/rxMsg_TIM_OdeOutput_Geometry.json index a0482b36f..cff628b73 100644 --- a/cv-data-service-library/src/test/resources/rxMsg_TIM_OdeOutput_Geometry.json +++ b/cv-data-service-library/src/test/resources/rxMsg_TIM_OdeOutput_Geometry.json @@ -69,10 +69,10 @@ } }, "durationTime": 32000, - "notUsed2": 0, - "notUsed3": 0, - "notUsed": 0, - "notUsed1": 0, + "doNotUse3": 0, + "doNotUse4": 0, + "doNotUse1": 0, + "doNotUse2": 0, "frameType": { "roadSignage": "" }, diff --git a/cv-data-service-library/src/test/resources/rxMsg_TIM_OdeOutput_MultipleRegions.json b/cv-data-service-library/src/test/resources/rxMsg_TIM_OdeOutput_MultipleRegions.json index 95472d2c5..1549e6c85 100644 --- a/cv-data-service-library/src/test/resources/rxMsg_TIM_OdeOutput_MultipleRegions.json +++ b/cv-data-service-library/src/test/resources/rxMsg_TIM_OdeOutput_MultipleRegions.json @@ -126,10 +126,10 @@ ] }, "durationTime": 32000, - "notUsed2": 0, - "notUsed3": 0, - "notUsed": 0, - "notUsed1": 0, + "doNotUse3": 0, + "doNotUse4": 0, + "doNotUse1": 0, + "doNotUse2": 0, "frameType": { "roadSignage": "" }, diff --git a/cv-data-service-library/src/test/resources/rxMsg_TIM_SpeedLimit.json b/cv-data-service-library/src/test/resources/rxMsg_TIM_SpeedLimit.json index 2e3e5efeb..4692d3167 100644 --- a/cv-data-service-library/src/test/resources/rxMsg_TIM_SpeedLimit.json +++ b/cv-data-service-library/src/test/resources/rxMsg_TIM_SpeedLimit.json @@ -82,10 +82,10 @@ } }, "durationTime": 32000, - "notUsed2": 0, - "notUsed3": 0, - "notUsed": 0, - "notUsed1": 0, + "doNotUse3": 0, + "doNotUse4": 0, + "doNotUse1": 0, + "doNotUse2": 0, "frameType": { "roadSignage": "" }, diff --git a/cv-data-service-library/src/test/resources/sdxDecodeResponse.json b/cv-data-service-library/src/test/resources/sdxDecodeResponse.json index dc4716bd7..0ee50ee8c 100644 --- a/cv-data-service-library/src/test/resources/sdxDecodeResponse.json +++ b/cv-data-service-library/src/test/resources/sdxDecodeResponse.json @@ -1,4 +1,4 @@ { "messageType": "MessageFrame", - "decodedMessage": "3113469004null1414344751-1101051425111111111111111120194690043200051westbound_I80_39.9_53.31_SAT-4035A5DF_RC_LYMI80EGRAD00414344751-11010514253270000000011111100000-1048300445417415787-1101068786414338729-1048297813417401082-1101173822414302517-1048296846417313218-1101278808414263469-1048334156417231472-1101362065414212124-1048363596417151273-1048348231417067803-1101430219414153700-1101515051414095801-1048292665416990008-1101609734414047121-1048303785416903264-1101704290413998478-1048330081416818875-1101798529413950074-1048362115416736381-1048394037416654255-1101901550413914121-1102014424413891332-1048426742416569080-1102126907413868840-1048459137416484285-1102235582413840132-1048492892416400669-1102332568413793094-1048526150416318091-1048559229416236049-1102434259413752451-1102543983413731407-1048592545416153296-1102655116413714865-1048625771416070802-1102767179413702216-1048658799415988821-1102881705413689256-1048694127415905527-1048732305415821992-1102996201413671479-1103105581413647716-1048769216415741004-1103216030413622307-1048806392415659600-1103325763413587617-1048843990415577334-11033794134135705071158955907null" + "decodedMessage": "3113469004null0414344751-1101051425111111111111111120194690043200050westbound_I80_39.9_53.31_SAT-4035A5DF_RC_LYMI80EGRAD00414344751-11010514253270000000011111100000-1048300445417415787-1101068786414338729-1048297813417401082-1101173822414302517-1048296846417313218-1101278808414263469-1048334156417231472-1101362065414212124-1048363596417151273-1048348231417067803-1101430219414153700-1101515051414095801-1048292665416990008-1101609734414047121-1048303785416903264-1101704290413998478-1048330081416818875-1101798529413950074-1048362115416736381-1048394037416654255-1101901550413914121-1102014424413891332-1048426742416569080-1102126907413868840-1048459137416484285-1102235582413840132-1048492892416400669-1102332568413793094-1048526150416318091-1048559229416236049-1102434259413752451-1102543983413731407-1048592545416153296-1102655116413714865-1048625771416070802-1102767179413702216-1048658799415988821-1102881705413689256-1048694127415905527-1048732305415821992-1102996201413671479-1103105581413647716-1048769216415741004-1103216030413622307-1048806392415659600-1103325763413587617-1048843990415577334-11033794134135705070058955907null" } diff --git a/cv-data-service-library/src/test/resources/tim_construction.json b/cv-data-service-library/src/test/resources/tim_construction.json index c47424c2d..8385e897d 100644 --- a/cv-data-service-library/src/test/resources/tim_construction.json +++ b/cv-data-service-library/src/test/resources/tim_construction.json @@ -28,90 +28,116 @@ "odeTimStartDateTime": "2021-03-04T00:00:00.000Z", "odeReceivedAt": "2021-03-23T18:24:18.438Z" }, - "payload": { - "data": { - "MessageFrame": { - "messageId": 31, - "value": { - "TravelerInformation": { - "timeStamp": 117744, - "packetID": "59CE5322A49CED33CB", - "urlB": null, - "dataFrames": { - "TravelerDataFrame": { - "regions": { - "GeographicalPath": { - "closedPath": { "false": "" }, - "anchor": { "lat": 417732928, "long": -1071000312 }, - "name": "I_I 80_SAT-BC1AC5E4_RW_24869%BUFF0-0", - "laneWidth": 5000, - "directionality": { "both": "" }, - "description": { - "path": { - "offset": { - "ll": { - "nodes": { - "NodeLL": [ - { "delta": { "node-LL1": { "lon": 0, "lat": 0 } } }, - { - "delta": { - "node-LL4": { "lon": 115529, "lat": -16624 } - } - }, - { - "delta": { - "node-LL4": { "lon": 39044, "lat": 1521 } - } - }, - { - "delta": { - "node-LL4": { "lon": 56228, "lat": 11450 } - } - } - ] - } + "payload": { + "data": { + "msgCnt": 1, + "timeStamp": 117744, + "packetID": "59CE5322A49CED33CB", + "urlB": "null", + "dataFrames": [ + { + "doNotUse1": 0, + "frameType": "advisory", + "msgId": { + "roadSignID": { + "position": { "lat": 417732928, "long": -1071000312 }, + "viewAngle": { + "from000-0to022-5degrees": true, + "from022-5to045-0degrees": true, + "from045-0to067-5degrees": true, + "from067-5to090-0degrees": true, + "from090-0to112-5degrees": true, + "from112-5to135-0degrees": true, + "from135-0to157-5degrees": true, + "from157-5to180-0degrees": true, + "from180-0to202-5degrees": true, + "from202-5to225-0degrees": true, + "from225-0to247-5degrees": true, + "from247-5to270-0degrees": true, + "from270-0to292-5degrees": true, + "from292-5to315-0degrees": true, + "from315-0to337-5degrees": true, + "from337-5to360-0degrees": true + }, + "mutcdCode": "warning" + } + }, + "durationTime": 32000, + "startYear": 2021, + "startTime": 89280, + "priority": 5, + "doNotUse2": 0, + "regions": [ + { + "name": "I_I 80_SAT-BC1AC5E4_RW_24869%BUFF0-0", + "id": { + "region": 0, + "id": 0 + }, + "anchor": { "lat": 417732928, "long": -1071000312 }, + "laneWidth": 5000, + "directionality": "both", + "closedPath": false, + "direction": { + "from000-0to022-5degrees": false, + "from022-5to045-0degrees": false, + "from045-0to067-5degrees": false, + "from067-5to090-0degrees": true, + "from090-0to112-5degrees": true, + "from112-5to135-0degrees": false, + "from135-0to157-5degrees": false, + "from157-5to180-0degrees": false, + "from180-0to202-5degrees": false, + "from202-5to225-0degrees": false, + "from225-0to247-5degrees": false, + "from247-5to270-0degrees": false, + "from270-0to292-5degrees": false, + "from292-5to315-0degrees": false, + "from315-0to337-5degrees": false, + "from337-5to360-0degrees": false + }, + "description": { + "path": { + "scale": 0, + "offset": { + "ll": { + "nodes": [ + { "delta": { "node-LL1": { "lon": 0, "lat": 0 } } }, + { + "delta": { + "node-LL4": { "lon": 115529, "lat": -16624 } } }, - "scale": 0 - } - }, - "id": { "id": 0, "region": 0 }, - "direction": "0001100000000000" - } - }, - "durationTime": 32000, - "notUsed2": 1, - "notUsed3": 1, - "startYear": 2021, - "msgId": { - "roadSignID": { - "viewAngle": 1111111111111111, - "mutcdCode": { "warning": "" }, - "position": { "lat": 417732928, "long": -1071000312 } - } - }, - "priority": 5, - "content": { - "workZone": { - "SEQUENCE": [ - { "item": { "itis": 1537 } }, - { "item": { "itis": 12554 } }, - { "item": { "itis": 8728 } } - ] - } - }, - "url": null, - "notUsed": 1, - "notUsed1": 1, - "frameType": { "advisory": "" }, - "startTime": 89280 - } - }, - "msgCnt": 1 - } - } - } - }, - "dataType": "TravelerInformation" - } -} + { + "delta": { + "node-LL4": { "lon": 39044, "lat": 1521 } + } + }, + { + "delta": { + "node-LL4": { "lon": 56228, "lat": 11450 } + } + } + ] + } + } + } + } + } + ], + "doNotUse3": 0, + "doNotUse4": 0, + "content": { + "workZone": [ + { "item": { "itis": 1537 } }, + { "item": { "itis": 12554 } }, + { "item": { "itis": 8728 } } + ] + }, + "url": "null" + } + ] + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.travelerinformation.TravelerInformation" + } +} \ No newline at end of file diff --git a/cv-data-service-library/src/test/resources/tim_construction_MultipleRegions.json b/cv-data-service-library/src/test/resources/tim_construction_MultipleRegions.json index c31beceec..2b0d145ab 100644 --- a/cv-data-service-library/src/test/resources/tim_construction_MultipleRegions.json +++ b/cv-data-service-library/src/test/resources/tim_construction_MultipleRegions.json @@ -1,157 +1,196 @@ { - "metadata": { - "request": { - "ode": { "verb": "POST", "version": 3 }, - "sdw": { - "recordId": "BC1AC5E4", - "serviceRegion": { - "nwCorner": { "latitude": 41.77329283, "longitude": -107.1000312 }, - "seCorner": { "latitude": 41.77163046, "longitude": -107.07895106 } - }, - "ttl": "oneyear" - } - }, - "recordGeneratedBy": "TMC", - "schemaVersion": 6, - "payloadType": "us.dot.its.jpo.ode.model.OdeTimPayload", - "odePacketID": "59CE5322A49CED33CB", - "serialId": { - "recordId": 0, - "serialNumber": 917, - "streamId": "9fa59341-9cab-403e-8306-6876238e9fda", - "bundleSize": 1, - "bundleId": 917 + "metadata": { + "request": { + "ode": { "verb": "POST", "version": 3 }, + "sdw": { + "recordId": "BC1AC5E4", + "serviceRegion": { + "nwCorner": { "latitude": 41.77329283, "longitude": -107.1000312 }, + "seCorner": { "latitude": 41.77163046, "longitude": -107.07895106 } }, - "sanitized": false, - "recordGeneratedAt": "2021-03-23T18:24:18.410648Z", - "maxDurationTime": 32000, - "odeTimStartDateTime": "2021-03-04T00:00:00.000Z", - "odeReceivedAt": "2021-03-23T18:24:18.438Z" + "ttl": "oneyear" + } + }, + "recordGeneratedBy": "TMC", + "schemaVersion": 6, + "payloadType": "us.dot.its.jpo.ode.model.OdeTimPayload", + "odePacketID": "59CE5322A49CED33CB", + "serialId": { + "recordId": 0, + "serialNumber": 917, + "streamId": "9fa59341-9cab-403e-8306-6876238e9fda", + "bundleSize": 1, + "bundleId": 917 }, - "payload": { - "data": { - "MessageFrame": { - "messageId": 31, - "value": { - "TravelerInformation": { - "timeStamp": 117744, - "packetID": "59CE5322A49CED33CB", - "urlB": null, - "dataFrames": { - "TravelerDataFrame": { - "regions": { - "GeographicalPath": [ - { - "closedPath": { "false": "" }, - "anchor": { "lat": 417732928, "long": -1071000312 }, - "name": "I_I 80_SAT-BC1AC5E4_RW_24869%BUFF0-0", - "laneWidth": 5000, - "directionality": { "both": "" }, - "description": { - "path": { - "offset": { - "ll": { - "nodes": { - "NodeLL": [ - { "delta": { "node-LL1": { "lon": 0, "lat": 0 } } }, - { - "delta": { - "node-LL4": { "lon": 115529, "lat": -16624 } - } - }, - { - "delta": { - "node-LL4": { "lon": 39044, "lat": 1521 } - } - }, - { - "delta": { - "node-LL4": { "lon": 56228, "lat": 11450 } - } - } - ] - } - } - }, - "scale": 0 - } - }, - "id": { "id": 0, "region": 0 }, - "direction": "0001100000000000" - }, - { - "closedPath": { "false": "" }, - "anchor": { "lat": 417732928, "long": -1071000312 }, - "name": "I_I 80_SAT-BC1AC5E4_RW_24869%BUFF0-0", - "laneWidth": 5000, - "directionality": { "both": "" }, - "description": { - "path": { - "offset": { - "ll": { - "nodes": { - "NodeLL": [ - { "delta": { "node-LL1": { "lon": 0, "lat": 0 } } }, - { - "delta": { - "node-LL4": { "lon": 115529, "lat": -16624 } - } - }, - { - "delta": { - "node-LL4": { "lon": 39044, "lat": 1521 } - } - }, - { - "delta": { - "node-LL4": { "lon": 56228, "lat": 11450 } - } - } - ] - } - } - }, - "scale": 0 - } - }, - "id": { "id": 0, "region": 0 }, - "direction": "0001100000000000" - } - ] - }, - "durationTime": 32000, - "notUsed2": 1, - "notUsed3": 1, - "startYear": 2021, - "msgId": { - "roadSignID": { - "viewAngle": 1111111111111111, - "mutcdCode": { "warning": "" }, - "position": { "lat": 417732928, "long": -1071000312 } - } - }, - "priority": 5, - "content": { - "workZone": { - "SEQUENCE": [ - { "item": { "itis": 1537 } }, - { "item": { "itis": 12554 } }, - { "item": { "itis": 8728 } } - ] - } - }, - "url": null, - "notUsed": 1, - "notUsed1": 1, - "frameType": { "advisory": "" }, - "startTime": 89280 - } + "sanitized": false, + "recordGeneratedAt": "2021-03-23T18:24:18.410648Z", + "maxDurationTime": 32000, + "odeTimStartDateTime": "2021-03-04T00:00:00.000Z", + "odeReceivedAt": "2021-03-23T18:24:18.438Z" + }, + "payload": { + "data": { + "msgCnt": 1, + "timeStamp": 117744, + "packetID": "59CE5322A49CED33CB", + "urlB": "null", + "dataFrames": [ + { + "doNotUse1": 0, + "frameType": "advisory", + "msgId": { + "roadSignID": { + "position": { "lat": 417732928, "long": -1071000312 }, + "viewAngle": { + "from000-0to022-5degrees": true, + "from022-5to045-0degrees": true, + "from045-0to067-5degrees": true, + "from067-5to090-0degrees": true, + "from090-0to112-5degrees": true, + "from112-5to135-0degrees": true, + "from135-0to157-5degrees": true, + "from157-5to180-0degrees": true, + "from180-0to202-5degrees": true, + "from202-5to225-0degrees": true, + "from225-0to247-5degrees": true, + "from247-5to270-0degrees": true, + "from270-0to292-5degrees": true, + "from292-5to315-0degrees": true, + "from315-0to337-5degrees": true, + "from337-5to360-0degrees": true + }, + "mutcdCode": "warning" + } + }, + "durationTime": 32000, + "startYear": 2021, + "startTime": 89280, + "priority": 5, + "doNotUse2": 0, + "regions": [ + { + "closedPath": false, + "anchor": { "lat": 417732928, "long": -1071000312 }, + "name": "I_I 80_SAT-BC1AC5E4_RW_24869%BUFF0-0", + "laneWidth": 5000, + "directionality": "both", + "description": { + "path": { + "offset": { + "ll": { + "nodes": [ + { "delta": { "node-LL1": { "lon": 0, "lat": 0 } } }, + { + "delta": { + "node-LL4": { "lon": 115529, "lat": -16624 } + } + }, + { + "delta": { + "node-LL4": { "lon": 39044, "lat": 1521 } + } }, - "msgCnt": 1 + { + "delta": { + "node-LL4": { "lon": 56228, "lat": 11450 } + } + } + ] } + }, + "scale": 0 } + }, + "id": { "id": 0, "region": 0 }, + "direction": { + "from000-0to022-5degrees": false, + "from022-5to045-0degrees": false, + "from045-0to067-5degrees": false, + "from067-5to090-0degrees": true, + "from090-0to112-5degrees": true, + "from112-5to135-0degrees": false, + "from135-0to157-5degrees": false, + "from157-5to180-0degrees": false, + "from180-0to202-5degrees": false, + "from202-5to225-0degrees": false, + "from225-0to247-5degrees": false, + "from247-5to270-0degrees": false, + "from270-0to292-5degrees": false, + "from292-5to315-0degrees": false, + "from315-0to337-5degrees": false, + "from337-5to360-0degrees": false + } + }, + { + "id": { + "region": 0, + "id": 0 + }, + "anchor": { "lat": 417732928, "long": -1071000312 }, + "name": "I_I 80_SAT-BC1AC5E4_RW_24869%BUFF0-0", + "laneWidth": 5000, + "directionality": "both", + "closedPath": false, + "direction": { + "from000-0to022-5degrees": false, + "from022-5to045-0degrees": false, + "from045-0to067-5degrees": false, + "from067-5to090-0degrees": true, + "from090-0to112-5degrees": true, + "from112-5to135-0degrees": false, + "from135-0to157-5degrees": false, + "from157-5to180-0degrees": false, + "from180-0to202-5degrees": false, + "from202-5to225-0degrees": false, + "from225-0to247-5degrees": false, + "from247-5to270-0degrees": false, + "from270-0to292-5degrees": false, + "from292-5to315-0degrees": false, + "from315-0to337-5degrees": false, + "from337-5to360-0degrees": false + }, + "description": { + "path": { + "scale": 0, + "offset": { + "ll": { + "nodes": [ + { "delta": { "node-LL1": { "lon": 0, "lat": 0 } } }, + { + "delta": { + "node-LL4": { "lon": 115529, "lat": -16624 } + } + }, + { + "delta": { + "node-LL4": { "lon": 39044, "lat": 1521 } + } + }, + { + "delta": { + "node-LL4": { "lon": 56228, "lat": 11450 } + } + } + ] + } + } + } + } } - }, - "dataType": "TravelerInformation" - } + ], + "doNotUse3": 0, + "doNotUse4": 0, + "content": { + "workZone": [ + { "item": { "itis": 1537 } }, + { "item": { "itis": 12554 } }, + { "item": { "itis": 8728 } } + ] + }, + "url": "null" + } + ] + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.travelerinformation.TravelerInformation" + } } diff --git a/cv-data-service-library/src/test/resources/tim_parking.json b/cv-data-service-library/src/test/resources/tim_parking.json index be3d8c709..519b60e3b 100644 --- a/cv-data-service-library/src/test/resources/tim_parking.json +++ b/cv-data-service-library/src/test/resources/tim_parking.json @@ -29,84 +29,105 @@ "odeReceivedAt": "2021-03-23T19:20:48.177Z" }, "payload": { - "data": { - "MessageFrame": { - "messageId": 31, - "value": { - "TravelerInformation": { - "timeStamp": 117800, - "packetID": "AE394DB5A033905B26", - "urlB": null, - "dataFrames": { - "TravelerDataFrame": { - "regions": { - "GeographicalPath": { - "closedPath": { "false": "" }, - "anchor": { "lat": 410985067, "long": -1051331761 }, - "name": "I_I 80_SAT-1AF9F730_P_68-0", - "laneWidth": 5000, - "directionality": { "both": "" }, - "description": { - "path": { - "offset": { - "ll": { - "nodes": { - "NodeLL": [ - { "delta": { "node-LL1": { "lon": 0, "lat": 0 } } }, - { - "delta": { - "node-LL5": { "lon": 133699, "lat": 163 } - } - }, - { - "delta": { - "node-LL4": { "lon": 56885, "lat": -5703 } - } - } - ] - } - } + "data": { + "msgCnt": 1, + "timeStamp": 117800, + "packetID": "AE394DB5A033905B26", + "urlB": null, + "dataFrames": [ + { + "doNotUse1": 0, + "frameType": "advisory", + "msgId": { + "roadSignID": { + "position": { "lat": 417732928, "long": -1071000312 }, + "viewAngle": { + "from000-0to022-5degrees": true, + "from022-5to045-0degrees": true, + "from045-0to067-5degrees": true, + "from067-5to090-0degrees": true, + "from090-0to112-5degrees": true, + "from112-5to135-0degrees": true, + "from135-0to157-5degrees": true, + "from157-5to180-0degrees": true, + "from180-0to202-5degrees": true, + "from202-5to225-0degrees": true, + "from225-0to247-5degrees": true, + "from247-5to270-0degrees": true, + "from270-0to292-5degrees": true, + "from292-5to315-0degrees": true, + "from315-0to337-5degrees": true, + "from337-5to360-0degrees": true + }, + "mutcdCode": "warning" + } + }, + "durationTime": 32000, + "startYear": 2021, + "startTime": 89280, + "priority": 5, + "doNotUse2": 0, + "regions": [ + { + "name": "I_I 25_SAT-A5FB00B7_VSL_V004988-0", + "id": { + "region": 0, + "id": 0 + }, + "anchor": { "lat": 417732928, "long": -1071000312 }, + "laneWidth": 5000, + "directionality": "both", + "closedPath": false, + "direction": { + "from000-0to022-5degrees": false, + "from022-5to045-0degrees": false, + "from045-0to067-5degrees": false, + "from067-5to090-0degrees": true, + "from090-0to112-5degrees": true, + "from112-5to135-0degrees": false, + "from135-0to157-5degrees": false, + "from157-5to180-0degrees": false, + "from180-0to202-5degrees": false, + "from202-5to225-0degrees": false, + "from225-0to247-5degrees": false, + "from247-5to270-0degrees": false, + "from270-0to292-5degrees": false, + "from292-5to315-0degrees": false, + "from315-0to337-5degrees": false, + "from337-5to360-0degrees": false + }, + "description": { + "path": { + "scale": 0, + "offset": { + "ll": { + "nodes": [ + { "node-LL1": { "lon": 0, "lat": 0 } }, + { + "node-LL5": { "lon": 133699, "lat": 163 } }, - "scale": 0 - } - }, - "id": { "id": 0, "region": 0 }, - "direction": "0001100000000000" - } - }, - "durationTime": 120, - "notUsed2": 1, - "notUsed3": 1, - "startYear": 2021, - "msgId": { - "roadSignID": { - "viewAngle": 1111111111111111, - "mutcdCode": { "warning": "" }, - "position": { "lat": 410985067, "long": -1051331761 } - } - }, - "priority": 5, - "content": { - "exitService": { - "SEQUENCE": [ - { "item": { "itis": 4104 } }, - { "item": { "itis": 11794 } }, - { "item": { "text": 345 } } - ] - } - }, - "url": null, - "notUsed": 1, - "notUsed1": 1, - "frameType": { "advisory": "" }, - "startTime": 117800 - } - }, - "msgCnt": 1 - } - } - } - }, - "dataType": "TravelerInformation" - } + { + "node-LL4": { "lon": 56885, "lat": -5703 } + } ] + } + } + } + } + } + ], + "doNotUse3": 0, + "doNotUse4": 0, + "content": { + "exitService": [ + { "item": { "itis": 4104 } }, + { "item": { "itis": 11794 } }, + { "item": { "text": 345 } } + ] + }, + "url": "null" + } + ] + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.travelerinformation.TravelerInformation" + } } diff --git a/cv-data-service-library/src/test/resources/tim_parking_MultipleRegions.json b/cv-data-service-library/src/test/resources/tim_parking_MultipleRegions.json index 2d18e11d7..594adfcc4 100644 --- a/cv-data-service-library/src/test/resources/tim_parking_MultipleRegions.json +++ b/cv-data-service-library/src/test/resources/tim_parking_MultipleRegions.json @@ -1,147 +1,187 @@ { - "metadata": { - "request": { - "ode": { "verb": "POST", "version": 3 }, - "sdw": { - "recordId": "1AF9F730", - "serviceRegion": { - "nwCorner": { "latitude": 41.09852308, "longitude": -105.13317609 }, - "seCorner": { "latitude": 41.09795279, "longitude": -105.11411777 } - }, - "ttl": "oneday" - } - }, - "recordGeneratedBy": "TMC", - "schemaVersion": 6, - "payloadType": "us.dot.its.jpo.ode.model.OdeTimPayload", - "odePacketID": "AE394DB5A033905B26", - "serialId": { - "recordId": 0, - "serialNumber": 922, - "streamId": "9fa59341-9cab-403e-8306-6876238e9fda", - "bundleSize": 1, - "bundleId": 922 + "metadata": { + "request": { + "ode": { "verb": "POST", "version": 3 }, + "sdw": { + "recordId": "1AF9F730", + "serviceRegion": { + "nwCorner": { "latitude": 41.09852308, "longitude": -105.13317609 }, + "seCorner": { "latitude": 41.09795279, "longitude": -105.11411777 } }, - "sanitized": false, - "recordGeneratedAt": "2021-03-23T19:20:48.130840Z", - "maxDurationTime": 120, - "odeTimStartDateTime": "2021-03-23T19:20:47.118Z", - "odeReceivedAt": "2021-03-23T19:20:48.177Z" + "ttl": "oneday" + } + }, + "recordGeneratedBy": "TMC", + "schemaVersion": 6, + "payloadType": "us.dot.its.jpo.ode.model.OdeTimPayload", + "odePacketID": "AE394DB5A033905B26", + "serialId": { + "recordId": 0, + "serialNumber": 922, + "streamId": "9fa59341-9cab-403e-8306-6876238e9fda", + "bundleSize": 1, + "bundleId": 922 }, - "payload": { - "data": { - "MessageFrame": { - "messageId": 31, - "value": { - "TravelerInformation": { - "timeStamp": 117800, - "packetID": "AE394DB5A033905B26", - "urlB": null, - "dataFrames": { - "TravelerDataFrame": { - "regions": { - "GeographicalPath": [ - { - "closedPath": { "false": "" }, - "anchor": { "lat": 410985067, "long": -1051331761 }, - "name": "I_I 80_SAT-1AF9F730_P_68-0", - "laneWidth": 5000, - "directionality": { "both": "" }, - "description": { - "path": { - "offset": { - "ll": { - "nodes": { - "NodeLL": [ - { "delta": { "node-LL1": { "lon": 0, "lat": 0 } } }, - { - "delta": { - "node-LL5": { "lon": 133699, "lat": 163 } - } - }, - { - "delta": { - "node-LL4": { "lon": 56885, "lat": -5703 } - } - } - ] - } - } - }, - "scale": 0 - } - }, - "id": { "id": 0, "region": 0 }, - "direction": "0001100000000000" - }, - { - "closedPath": { "false": "" }, - "anchor": { "lat": 410985067, "long": -1051331761 }, - "name": "I_I 80_SAT-1AF9F730_P_68-0", - "laneWidth": 5000, - "directionality": { "both": "" }, - "description": { - "path": { - "offset": { - "ll": { - "nodes": { - "NodeLL": [ - { "delta": { "node-LL1": { "lon": 0, "lat": 0 } } }, - { - "delta": { - "node-LL5": { "lon": 133699, "lat": 163 } - } - }, - { - "delta": { - "node-LL4": { "lon": 56885, "lat": -5703 } - } - } - ] - } - } - }, - "scale": 0 - } - }, - "id": { "id": 0, "region": 0 }, - "direction": "0001100000000000" - } - ] - }, - "durationTime": 120, - "notUsed2": 1, - "notUsed3": 1, - "startYear": 2021, - "msgId": { - "roadSignID": { - "viewAngle": 1111111111111111, - "mutcdCode": { "warning": "" }, - "position": { "lat": 410985067, "long": -1051331761 } - } - }, - "priority": 5, - "content": { - "exitService": { - "SEQUENCE": [ - { "item": { "itis": 4104 } }, - { "item": { "itis": 11794 } }, - { "item": { "text": 345 } } - ] - } - }, - "url": null, - "notUsed": 1, - "notUsed1": 1, - "frameType": { "advisory": "" }, - "startTime": 117800 - } - }, - "msgCnt": 1 + "sanitized": false, + "recordGeneratedAt": "2021-03-23T19:20:48.130840Z", + "maxDurationTime": 120, + "odeTimStartDateTime": "2021-03-23T19:20:47.118Z", + "odeReceivedAt": "2021-03-23T19:20:48.177Z" + }, + "payload": { + "data": { + "timeStamp": 117800, + "packetID": "AE394DB5A033905B26", + "urlB": null, + "dataFrames": [ + { + "regions": [ + { + "closedPath": false, + "anchor": { "lat": 410985067, "long": -1051331761 }, + "name": "I_I 80_SAT-1AF9F730_P_68-0", + "laneWidth": 5000, + "directionality": "both", + "description": { + "path": { + "offset": { + "ll": { + "nodes": [ + { + "delta": { "node-LL1": { "lon": 0, "lat": 0 } } + }, + { + "delta": { + "node-LL5": { "lon": 133699, "lat": 163 } + } + }, + { + "delta": { + "node-LL4": { "lon": 56885, "lat": -5703 } + } + } + ] + } + }, + "scale": 0 } + }, + "id": { "id": 0, "region": 0 }, + "direction": { + "from000-0to022-5degrees": false, + "from022-5to045-0degrees": false, + "from045-0to067-5degrees": false, + "from067-5to090-0degrees": true, + "from090-0to112-5degrees": true, + "from112-5to135-0degrees": false, + "from135-0to157-5degrees": false, + "from157-5to180-0degrees": false, + "from180-0to202-5degrees": false, + "from202-5to225-0degrees": false, + "from225-0to247-5degrees": false, + "from247-5to270-0degrees": false, + "from270-0to292-5degrees": false, + "from292-5to315-0degrees": false, + "from315-0to337-5degrees": false, + "from337-5to360-0degrees": false } - } - }, - "dataType": "TravelerInformation" - } + }, + { + "closedPath": "false", + "anchor": { "lat": 410985067, "long": -1051331761 }, + "name": "I_I 80_SAT-1AF9F730_P_68-0", + "laneWidth": 5000, + "directionality": "both", + "description": { + "path": { + "offset": { + "ll": { + "nodes": [ + { + "delta": { "node-LL1": { "lon": 0, "lat": 0 } } + }, + { + "delta": { + "node-LL5": { "lon": 133699, "lat": 163 } + } + }, + { + "delta": { + "node-LL4": { "lon": 56885, "lat": -5703 } + } + } + ] + } + }, + "scale": 0 + } + }, + "id": { "id": 0, "region": 0 }, + "direction": { + "from000-0to022-5degrees": false, + "from022-5to045-0degrees": false, + "from045-0to067-5degrees": false, + "from067-5to090-0degrees": true, + "from090-0to112-5degrees": true, + "from112-5to135-0degrees": false, + "from135-0to157-5degrees": false, + "from157-5to180-0degrees": false, + "from180-0to202-5degrees": false, + "from202-5to225-0degrees": false, + "from225-0to247-5degrees": false, + "from247-5to270-0degrees": false, + "from270-0to292-5degrees": false, + "from292-5to315-0degrees": false, + "from315-0to337-5degrees": false, + "from337-5to360-0degrees": false + } + } + ], + "durationTime": 120, + "doNotUse3": 0, + "doNotUse4": 0, + "startYear": 2021, + "msgId": { + "roadSignID": { + "viewAngle": { + "from000-0to022-5degrees": true, + "from022-5to045-0degrees": true, + "from045-0to067-5degrees": true, + "from067-5to090-0degrees": true, + "from090-0to112-5degrees": true, + "from112-5to135-0degrees": true, + "from135-0to157-5degrees": true, + "from157-5to180-0degrees": true, + "from180-0to202-5degrees": true, + "from202-5to225-0degrees": true, + "from225-0to247-5degrees": true, + "from247-5to270-0degrees": true, + "from270-0to292-5degrees": true, + "from292-5to315-0degrees": true, + "from315-0to337-5degrees": true, + "from337-5to360-0degrees": true + }, + "mutcdCode": "warning", + "position": { "lat": 410985067, "long": -1051331761 } + } + }, + "priority": 5, + "content": { + "exitService": [ + { "item": { "itis": 4104 } }, + { "item": { "itis": 11794 } }, + { "item": { "text": 345 } } + ] + }, + "url": "null", + "doNotUse1": 0, + "doNotUse2": 0, + "frameType": "advisory", + "startTime": 117800 + } + ], + "msgCnt": 1 + }, + "dataType": "TravelerInformation" + } } diff --git a/cv-data-service-library/src/test/resources/tim_vsl.json b/cv-data-service-library/src/test/resources/tim_vsl.json index 3621812d5..fde8dd0cb 100644 --- a/cv-data-service-library/src/test/resources/tim_vsl.json +++ b/cv-data-service-library/src/test/resources/tim_vsl.json @@ -1,102 +1,126 @@ { - "metadata": { - "request": { - "ode": { "verb": "POST", "version": 3 }, - "sdw": { - "recordId": "A5FB00B7", - "serviceRegion": { - "nwCorner": { "latitude": 42.8616018, "longitude": -106.3393164 }, - "seCorner": { "latitude": 42.8616018, "longitude": -106.3393164 } - }, - "ttl": "oneyear" - } - }, - "recordGeneratedBy": "TMC", - "schemaVersion": 6, - "payloadType": "us.dot.its.jpo.ode.model.OdeTimPayload", - "odePacketID": "FCEAC42FC4F3A9E274", - "serialId": { - "recordId": 0, - "serialNumber": 5745, - "streamId": "28dcab17-fb6b-4df5-8139-6cf59e5d64c5", - "bundleSize": 1, - "bundleId": 5745 + "metadata": { + "request": { + "ode": { "verb": "POST", "version": 3 }, + "sdw": { + "recordId": "A5FB00B7", + "serviceRegion": { + "nwCorner": { "latitude": 42.8616018, "longitude": -106.3393164 }, + "seCorner": { "latitude": 42.8616018, "longitude": -106.3393164 } }, - "sanitized": false, - "recordGeneratedAt": "2021-03-22T08:41:37.953583Z", - "maxDurationTime": 32000, - "odeTimStartDateTime": "2021-03-22T08:41:36.848Z", - "odeReceivedAt": "2021-03-22T08:41:38.108Z" + "ttl": "oneyear" + } + }, + "recordGeneratedBy": "TMC", + "schemaVersion": 6, + "payloadType": "us.dot.its.jpo.ode.model.OdeTimPayload", + "odePacketID": "FCEAC42FC4F3A9E274", + "serialId": { + "recordId": 0, + "serialNumber": 5745, + "streamId": "28dcab17-fb6b-4df5-8139-6cf59e5d64c5", + "bundleSize": 1, + "bundleId": 5745 }, - "payload": { - "data": { - "MessageFrame": { - "messageId": 31, - "value": { - "TravelerInformation": { - "timeStamp": 115721, - "packetID": "FCEAC42FC4F3A9E274", - "urlB": null, - "dataFrames": { - "TravelerDataFrame": { - "regions": { - "GeographicalPath": { - "closedPath": { "false": "" }, - "anchor": { "lat": 428605724, "long": -1063379489 }, - "name": "I_I 25_SAT-A5FB00B7_VSL_V004988-0", - "laneWidth": 5000, - "directionality": { "both": "" }, - "description": { - "path": { - "offset": { - "ll": { - "nodes": { - "NodeLL": { - "delta": { "node-LL3": { "lon": -13675, "lat": 10295 } } - } - } - } - }, - "scale": 0 - } - }, - "id": { "id": 0, "region": 0 }, - "direction": "0000000000000010" - } - }, - "durationTime": 32000, - "notUsed2": 1, - "notUsed3": 1, - "startYear": 2021, - "msgId": { - "roadSignID": { - "viewAngle": 1111111111111111, - "mutcdCode": { "regulatory": "" }, - "position": { "lat": 428605724, "long": -1063379489 } - } - }, - "priority": 5, - "content": { - "speedLimit": { - "SEQUENCE": [ - { "item": { "itis": 268 } }, - { "item": { "itis": 12604 } }, - { "item": { "itis": 8720 } } - ] - } - }, - "url": null, - "notUsed": 1, - "notUsed1": 1, - "frameType": { "roadSignage": "" }, - "startTime": 115721 - } - }, - "msgCnt": 1 + "sanitized": false, + "recordGeneratedAt": "2021-03-22T08:41:37.953583Z", + "maxDurationTime": 32000, + "odeTimStartDateTime": "2021-03-22T08:41:36.848Z", + "odeReceivedAt": "2021-03-22T08:41:38.108Z" + }, + "payload": { + "data": { + "msgCnt": 1, + "timeStamp": 115721, + "packetID": "FCEAC42FC4F3A9E274", + "urlB": "null", + "dataFrames": [ + { + "doNotUse1": 0, + "frameType": "advisory", + "msgId": { + "roadSignID": { + "position": { "lat": 417732928, "long": -1071000312 }, + "viewAngle": { + "from000-0to022-5degrees": true, + "from022-5to045-0degrees": true, + "from045-0to067-5degrees": true, + "from067-5to090-0degrees": true, + "from090-0to112-5degrees": true, + "from112-5to135-0degrees": true, + "from135-0to157-5degrees": true, + "from157-5to180-0degrees": true, + "from180-0to202-5degrees": true, + "from202-5to225-0degrees": true, + "from225-0to247-5degrees": true, + "from247-5to270-0degrees": true, + "from270-0to292-5degrees": true, + "from292-5to315-0degrees": true, + "from315-0to337-5degrees": true, + "from337-5to360-0degrees": true + }, + "mutcdCode": "warning" + } + }, + "durationTime": 32000, + "startYear": 2021, + "startTime": 89280, + "priority": 5, + "doNotUse2": 0, + "regions": [ + { + "name": "I_I 25_SAT-A5FB00B7_VSL_V004988-0", + "id": { + "region": 0, + "id": 0 + }, + "anchor": { "lat": 417732928, "long": -1071000312 }, + "laneWidth": 5000, + "directionality": "both", + "closedPath": false, + "direction": { + "from000-0to022-5degrees": false, + "from022-5to045-0degrees": false, + "from045-0to067-5degrees": false, + "from067-5to090-0degrees": true, + "from090-0to112-5degrees": true, + "from112-5to135-0degrees": false, + "from135-0to157-5degrees": false, + "from157-5to180-0degrees": false, + "from180-0to202-5degrees": false, + "from202-5to225-0degrees": false, + "from225-0to247-5degrees": false, + "from247-5to270-0degrees": false, + "from270-0to292-5degrees": false, + "from292-5to315-0degrees": false, + "from315-0to337-5degrees": false, + "from337-5to360-0degrees": false + }, + "description": { + "path": { + "scale": 0, + "offset": { + "ll": { + "nodes": [{ "node-LL3": { "lon": -13675, "lat": 10295 } }] } + } } + } } - }, - "dataType": "TravelerInformation" - } + ], + "doNotUse3": 0, + "doNotUse4": 0, + "content": { + "speedLimit": [ + { "item": { "itis": 268 } }, + { "item": { "itis": 12604 } }, + { "item": { "itis": 8720 } } + ] + }, + "url": "null" + } + ] + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.travelerinformation.TravelerInformation" + } } diff --git a/cv-data-service-library/src/test/resources/tim_vsl_MultipleRegions.json b/cv-data-service-library/src/test/resources/tim_vsl_MultipleRegions.json index c8d6cde06..e8220debf 100644 --- a/cv-data-service-library/src/test/resources/tim_vsl_MultipleRegions.json +++ b/cv-data-service-library/src/test/resources/tim_vsl_MultipleRegions.json @@ -1,127 +1,171 @@ { - "metadata": { - "request": { - "ode": { "verb": "POST", "version": 3 }, - "sdw": { - "recordId": "A5FB00B7", - "serviceRegion": { - "nwCorner": { "latitude": 42.8616018, "longitude": -106.3393164 }, - "seCorner": { "latitude": 42.8616018, "longitude": -106.3393164 } - }, - "ttl": "oneyear" - } - }, - "recordGeneratedBy": "TMC", - "schemaVersion": 6, - "payloadType": "us.dot.its.jpo.ode.model.OdeTimPayload", - "odePacketID": "FCEAC42FC4F3A9E274", - "serialId": { - "recordId": 0, - "serialNumber": 5745, - "streamId": "28dcab17-fb6b-4df5-8139-6cf59e5d64c5", - "bundleSize": 1, - "bundleId": 5745 + "metadata": { + "request": { + "ode": { "verb": "POST", "version": 3 }, + "sdw": { + "recordId": "A5FB00B7", + "serviceRegion": { + "nwCorner": { "latitude": 42.8616018, "longitude": -106.3393164 }, + "seCorner": { "latitude": 42.8616018, "longitude": -106.3393164 } }, - "sanitized": false, - "recordGeneratedAt": "2021-03-22T08:41:37.953583Z", - "maxDurationTime": 32000, - "odeTimStartDateTime": "2021-03-22T08:41:36.848Z", - "odeReceivedAt": "2021-03-22T08:41:38.108Z" + "ttl": "oneyear" + } }, - "payload": { - "data": { - "MessageFrame": { - "messageId": 31, - "value": { - "TravelerInformation": { - "timeStamp": 115721, - "packetID": "FCEAC42FC4F3A9E274", - "urlB": null, - "dataFrames": { - "TravelerDataFrame": { - "regions": { - "GeographicalPath": [ - { - "closedPath": { "false": "" }, - "anchor": { "lat": 428605724, "long": -1063379489 }, - "name": "I_I 25_SAT-A5FB00B7_VSL_V004988-0", - "laneWidth": 5000, - "directionality": { "both": "" }, - "description": { - "path": { - "offset": { - "ll": { - "nodes": { - "NodeLL": { - "delta": { "node-LL3": { "lon": -13675, "lat": 10295 } } - } - } - } - }, - "scale": 0 - } - }, - "id": { "id": 0, "region": 0 }, - "direction": "0000000000000010" - }, - { - "closedPath": { "false": "" }, - "anchor": { "lat": 428605724, "long": -1063379489 }, - "name": "I_I 25_SAT-A5FB00B7_VSL_V004988-0", - "laneWidth": 5000, - "directionality": { "both": "" }, - "description": { - "path": { - "offset": { - "ll": { - "nodes": { - "NodeLL": { - "delta": { "node-LL3": { "lon": -13675, "lat": 10295 } } - } - } - } - }, - "scale": 0 - } - }, - "id": { "id": 0, "region": 0 }, - "direction": "0000000000000010" - } - ] - }, - "durationTime": 32000, - "notUsed2": 1, - "notUsed3": 1, - "startYear": 2021, - "msgId": { - "roadSignID": { - "viewAngle": 1111111111111111, - "mutcdCode": { "regulatory": "" }, - "position": { "lat": 428605724, "long": -1063379489 } - } - }, - "priority": 5, - "content": { - "speedLimit": { - "SEQUENCE": [ - { "item": { "itis": 268 } }, - { "item": { "itis": 12604 } }, - { "item": { "itis": 8720 } } - ] - } - }, - "url": null, - "notUsed": 1, - "notUsed1": 1, - "frameType": { "roadSignage": "" }, - "startTime": 115721 - } - }, - "msgCnt": 1 + "recordGeneratedBy": "TMC", + "schemaVersion": 6, + "payloadType": "us.dot.its.jpo.ode.model.OdeTimPayload", + "odePacketID": "FCEAC42FC4F3A9E274", + "serialId": { + "recordId": 0, + "serialNumber": 5745, + "streamId": "28dcab17-fb6b-4df5-8139-6cf59e5d64c5", + "bundleSize": 1, + "bundleId": 5745 + }, + "sanitized": false, + "recordGeneratedAt": "2021-03-22T08:41:37.953583Z", + "maxDurationTime": 32000, + "odeTimStartDateTime": "2021-03-22T08:41:36.848Z", + "odeReceivedAt": "2021-03-22T08:41:38.108Z" + }, + "payload": { + "data": { + "timeStamp": 115721, + "packetID": "FCEAC42FC4F3A9E274", + "urlB": "null", + "dataFrames": [ + { + "regions": [ + { + "closedPath": "false", + "anchor": { "lat": 428605724, "long": -1063379489 }, + "name": "I_I 25_SAT-A5FB00B7_VSL_V004988-0", + "laneWidth": 5000, + "directionality": "both", + "description": { + "path": { + "offset": { + "ll": { + "nodes": [ + { + "delta": { + "node-LL3": { "lon": -13675, "lat": 10295 } + } + } + ] + } + }, + "scale": 0 + } + }, + "id": { "id": 0, "region": 0 }, + "direction": { + "from000-0to022-5degrees": false, + "from022-5to045-0degrees": false, + "from045-0to067-5degrees": false, + "from067-5to090-0degrees": false, + "from090-0to112-5degrees": false, + "from112-5to135-0degrees": false, + "from135-0to157-5degrees": false, + "from157-5to180-0degrees": false, + "from180-0to202-5degrees": false, + "from202-5to225-0degrees": false, + "from225-0to247-5degrees": false, + "from247-5to270-0degrees": false, + "from270-0to292-5degrees": false, + "from292-5to315-0degrees": false, + "from315-0to337-5degrees": true, + "from337-5to360-0degrees": false + } + }, + { + "closedPath": "false", + "anchor": { "lat": 428605724, "long": -1063379489 }, + "name": "I_I 25_SAT-A5FB00B7_VSL_V004988-0", + "laneWidth": 5000, + "directionality": "both", + "description": { + "path": { + "offset": { + "ll": { + "nodes": [ + { + "delta": { + "node-LL3": { "lon": -13675, "lat": 10295 } + } + } + ] } + }, + "scale": 0 } + }, + "id": { "id": 0, "region": 0 }, + "direction": { + "from000-0to022-5degrees": false, + "from022-5to045-0degrees": false, + "from045-0to067-5degrees": false, + "from067-5to090-0degrees": false, + "from090-0to112-5degrees": false, + "from112-5to135-0degrees": false, + "from135-0to157-5degrees": false, + "from157-5to180-0degrees": false, + "from180-0to202-5degrees": false, + "from202-5to225-0degrees": false, + "from225-0to247-5degrees": false, + "from247-5to270-0degrees": false, + "from270-0to292-5degrees": false, + "from292-5to315-0degrees": false, + "from315-0to337-5degrees": true, + "from337-5to360-0degrees": false + } } - }, - "dataType": "TravelerInformation" - } + ], + "durationTime": 32000, + "doNotUse3": 1, + "doNotUse4": 1, + "startYear": 2021, + "msgId": { + "roadSignID": { + "viewAngle": { + "from000-0to022-5degrees": true, + "from022-5to045-0degrees": true, + "from045-0to067-5degrees": true, + "from067-5to090-0degrees": true, + "from090-0to112-5degrees": true, + "from112-5to135-0degrees": true, + "from135-0to157-5degrees": true, + "from157-5to180-0degrees": true, + "from180-0to202-5degrees": true, + "from202-5to225-0degrees": true, + "from225-0to247-5degrees": true, + "from247-5to270-0degrees": true, + "from270-0to292-5degrees": true, + "from292-5to315-0degrees": true, + "from315-0to337-5degrees": true, + "from337-5to360-0degrees": true + }, + "mutcdCode": "regulatory", + "position": { "lat": 428605724, "long": -1063379489 } + } + }, + "priority": 5, + "content": { + "speedLimit": [ + { "item": { "itis": 268 } }, + { "item": { "itis": 12604 } }, + { "item": { "itis": 8720 } } + ] + }, + "url": "null", + "doNotUse1": 0, + "doNotUse2": 0, + "frameType": "roadSignage", + "startTime": 115721 + } + ], + "msgCnt": 1 + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.travelerinformation.TravelerInformation" + } } diff --git a/cv-data-tasks/Dockerfile b/cv-data-tasks/Dockerfile index fb6a92067..4fc8fa7a9 100644 --- a/cv-data-tasks/Dockerfile +++ b/cv-data-tasks/Dockerfile @@ -1,13 +1,13 @@ # Required resources for deployment: -# - cv-data-tasks-1.4.0-SNAPSHOT.jar +# - cv-data-tasks-2.0.0.jar FROM maven:3.8-eclipse-temurin-21-alpine -ENV install_dir /home/wyocv/wyocv_applications/cv-data-tasks +ENV install_dir /home/timm/timm_applications/cv-data-tasks ADD . $install_dir # Uncomment and modify the certificate name on the following line to import a certificate into the Java keystore # RUN keytool -import -alias resdf -storepass changeit -noprompt -cacerts -file ${install_dir}/.cer -CMD java -jar ${install_dir}/cv-data-tasks-1.4.0-SNAPSHOT.jar +CMD java -jar ${install_dir}/cv-data-tasks-2.0.0.jar diff --git a/cv-data-tasks/README.md b/cv-data-tasks/README.md index 24b13e7e2..6a60f2f4b 100644 --- a/cv-data-tasks/README.md +++ b/cv-data-tasks/README.md @@ -87,7 +87,7 @@ mvn test ## Deployment -This application is deployed using Docker, and is part of the larger WyoCVApplication suite. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. +This application is deployed using Docker, and is part of the larger TIM Manager. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. # Configuration Reference diff --git a/cv-data-tasks/pom.xml b/cv-data-tasks/pom.xml index 43d6188c4..5fb8b54e6 100644 --- a/cv-data-tasks/pom.xml +++ b/cv-data-tasks/pom.xml @@ -8,9 +8,9 @@ - com.wyocv - wyo-cv - 1.4.0-SNAPSHOT + com.timm + tim-manager + 2.0.0 diff --git a/cv-data-tasks/src/main/java/com/trihydro/tasks/Application.java b/cv-data-tasks/src/main/java/com/trihydro/tasks/Application.java index 5c08cbbf5..a4a8a8e6c 100644 --- a/cv-data-tasks/src/main/java/com/trihydro/tasks/Application.java +++ b/cv-data-tasks/src/main/java/com/trihydro/tasks/Application.java @@ -1,5 +1,8 @@ + package com.trihydro.tasks; +import com.trihydro.tasks.actions.CleanupStaleActiveTimHoldingRecords; +import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; import java.io.IOException; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -43,6 +46,7 @@ import com.trihydro.tasks.actions.VerifyHSMFunctional; import com.trihydro.tasks.config.DataTasksConfiguration; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -56,85 +60,91 @@ TimGenerationHelper.class, PathNodeLLService.class, MilepostService.class, MilepostReduction.class, RegionService.class, RsuService.class, OdeService.class, ActiveTimHoldingService.class, WydotTimService.class, TimTypeService.class, CreateBaseTimUtil.class, TimRsuService.class, - SnmpHelper.class, RegionNameTrimmer.class }) + SnmpHelper.class, RegionNameTrimmer.class, IdenticalPointsExceptionHandler.class }) +@Slf4j public class Application { - protected static DataTasksConfiguration config; - - private RemoveExpiredActiveTims removeExpiredActiveTims; - private CleanupActiveTims cleanupActiveTims; - private ValidateSdx sdxValidator; - private ValidateRsus rsuValidator; - private ValidateTmdd tmddValidator; - private VerifyHSMFunctional hsmFunctional; - RetentionPolicyEnforcement retentionEnforcement; - private Utility utility; - - @Autowired - public void InjectDependencies(DataTasksConfiguration _config, RemoveExpiredActiveTims _removeExpiredActiveTims, - CleanupActiveTims _cleanupActiveTims, ValidateSdx _sdxValidator, - ValidateRsus _rsuValidator, ValidateTmdd _tmddValidator, - RetentionPolicyEnforcement _retentionEnforcement, VerifyHSMFunctional _hsmFunctional, - Utility _utility) { - config = _config; - removeExpiredActiveTims = _removeExpiredActiveTims; - cleanupActiveTims = _cleanupActiveTims; - sdxValidator = _sdxValidator; - rsuValidator = _rsuValidator; - tmddValidator = _tmddValidator; - retentionEnforcement = _retentionEnforcement; - hsmFunctional = _hsmFunctional; - utility = _utility; + protected static DataTasksConfiguration config; + + private final RemoveExpiredActiveTims removeExpiredActiveTims; + private final CleanupActiveTims cleanupActiveTims; + private final ValidateSdx sdxValidator; + private final ValidateRsus rsuValidator; + private final ValidateTmdd tmddValidator; + private final VerifyHSMFunctional hsmFunctional; + private final RetentionPolicyEnforcement retentionEnforcement; + private final CleanupStaleActiveTimHoldingRecords cleanupStaleActiveTimHoldingRecords; + + @Autowired + public Application(DataTasksConfiguration config, RemoveExpiredActiveTims removeExpiredActiveTims, CleanupActiveTims cleanupActiveTims, ValidateSdx sdxValidator, ValidateRsus rsuValidator, + ValidateTmdd tmddValidator, RetentionPolicyEnforcement retentionEnforcement, VerifyHSMFunctional hsmFunctional, + CleanupStaleActiveTimHoldingRecords cleanupStaleActiveTimHoldingRecords) { + Application.config = config; + this.removeExpiredActiveTims = removeExpiredActiveTims; + this.cleanupActiveTims = cleanupActiveTims; + this.sdxValidator = sdxValidator; + this.rsuValidator = rsuValidator; + this.tmddValidator = tmddValidator; + this.retentionEnforcement = retentionEnforcement; + this.hsmFunctional = hsmFunctional; + this.cleanupStaleActiveTimHoldingRecords = cleanupStaleActiveTimHoldingRecords; + } + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + @PostConstruct + public void run() throws IOException { + ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(4); + + // Remove Expired Active Tims + log.info("Scheduling Remove Expired Active Tims..."); + scheduledExecutorService.scheduleAtFixedRate(removeExpiredActiveTims, 0, config.getRemoveExpiredPeriodMinutes(), TimeUnit.MINUTES); + + // Cleanup Active Tims + log.info("Scheduling Cleanup Active Tims..."); + scheduledExecutorService.scheduleAtFixedRate(cleanupActiveTims, 5, config.getCleanupPeriodMinutes(), TimeUnit.MINUTES); + + // SDX Validator + log.info("Scheduling SDX Validator..."); + scheduledExecutorService.scheduleAtFixedRate(sdxValidator, 15, config.getSdxValidationPeriodMinutes(), TimeUnit.MINUTES); + + // HSM Check + if (config.getRunHsmCheck()) { + log.info("HSM check configured, scheduling..."); + scheduledExecutorService.scheduleAtFixedRate(hsmFunctional, 0, config.getHsmFunctionalityMinutes(), TimeUnit.MINUTES); + } else { + log.info("HSM check not configured, skipping..."); } - public static void main(String[] args) { - SpringApplication.run(Application.class, args); + // RSU Validator + // Since we're validating Active Tims from both environments in the same task, + // we only want this running in 1 environment, or else we'll receive duplicate + // emails + if (config.getRunRsuValidation()) { + log.info("Scheduling RSU Validator..."); + scheduledExecutorService.scheduleAtFixedRate(rsuValidator, 20, config.getRsuValidationPeriodMinutes(), TimeUnit.MINUTES); + } else { + log.info("RSU Validation not configured, skipping..."); } - @PostConstruct - public void run() throws IOException { - ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(4); - - // Remove Expired Active Tims - scheduledExecutorService.scheduleAtFixedRate(removeExpiredActiveTims, 0, - config.getRemoveExpiredPeriodMinutes(), TimeUnit.MINUTES); - - // Cleanup Active Tims - scheduledExecutorService.scheduleAtFixedRate(cleanupActiveTims, 5, config.getCleanupPeriodMinutes(), - TimeUnit.MINUTES); - - // SDX Validator - scheduledExecutorService.scheduleAtFixedRate(sdxValidator, 15, config.getSdxValidationPeriodMinutes(), - TimeUnit.MINUTES); - - // HSM Check - if (config.getRunHsmCheck()) { - utility.logWithDate("HSM check configured, scheduling..."); - scheduledExecutorService.scheduleAtFixedRate(hsmFunctional, 0, - config.getHsmFunctionalityMinutes(), TimeUnit.MINUTES); - } else { - utility.logWithDate("HSM check not configured, skipping..."); - } - - // RSU Validator - // Since we're validating Active Tims from both environments in the same task, - // we only want this running in 1 environment, or else we'll receive duplicate - // emails - if (config.getRunRsuValidation()) { - scheduledExecutorService.scheduleAtFixedRate(rsuValidator, 20, - config.getRsuValidationPeriodMinutes(), TimeUnit.MINUTES); - } - - // TMDD Validator - // Since dev has many Active TIMs that aren't present on the TMDD, - // we should only be running the TMDD validation in prod. - if (config.getRunTmddValidation()) { - scheduledExecutorService.scheduleAtFixedRate(tmddValidator, 25, - config.getTmddValidationPeriodMinutes(), TimeUnit.MINUTES); - } - - // Retention Policy Enforcement - scheduledExecutorService.scheduleAtFixedRate(retentionEnforcement, 30, - config.getRetentionEnforcementPeriodMinutes(), TimeUnit.MINUTES); + // TMDD Validator + // Since dev has many Active TIMs that aren't present on the TMDD, + // we should only be running the TMDD validation in prod. + if (config.getRunTmddValidation()) { + log.info("Scheduling TMDD Validator..."); + scheduledExecutorService.scheduleAtFixedRate(tmddValidator, 25, config.getTmddValidationPeriodMinutes(), TimeUnit.MINUTES); + } else { + log.info("TMDD Validation not configured, skipping..."); } + + // Retention Policy Enforcement + log.info("Scheduling Retention Policy Enforcement..."); + scheduledExecutorService.scheduleAtFixedRate(retentionEnforcement, 30, config.getRetentionEnforcementPeriodMinutes(), TimeUnit.MINUTES); + + // Cleanup Stale Active Tim Holding Records + log.info("Scheduling Cleanup Stale Active Tim Holding Records to run every {} minutes...", config.getCleanupStaleActiveTimHoldingRecordsPeriodMinutes()); + scheduledExecutorService.scheduleAtFixedRate(cleanupStaleActiveTimHoldingRecords, 0, config.getCleanupStaleActiveTimHoldingRecordsPeriodMinutes(), TimeUnit.MINUTES); + } } \ No newline at end of file diff --git a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/CleanupStaleActiveTimHoldingRecords.java b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/CleanupStaleActiveTimHoldingRecords.java new file mode 100644 index 000000000..7dbcec91e --- /dev/null +++ b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/CleanupStaleActiveTimHoldingRecords.java @@ -0,0 +1,189 @@ +package com.trihydro.tasks.actions; + +import com.trihydro.library.model.ActiveTim; +import com.trihydro.library.model.ActiveTimHolding; +import com.trihydro.library.service.ActiveTimHoldingService; +import com.trihydro.library.service.ActiveTimService; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class CleanupStaleActiveTimHoldingRecords implements Runnable { + + private final ActiveTimHoldingService activeTimHoldingService; + private final ActiveTimService activeTimService; + + // Set of likely stale records identified by active tim holding id (this will be updated at the end of each run) + private final Set staleRecordsIdentifiedLastRun = new HashSet<>(); + + @Autowired + public CleanupStaleActiveTimHoldingRecords(ActiveTimHoldingService activeTimHoldingService, ActiveTimService activeTimService) { + this.activeTimHoldingService = activeTimHoldingService; + this.activeTimService = activeTimService; + } + + /** + * The run method is executed periodically to clean up stale ActiveTimHolding records. + * It performs the following steps: + * 1. Retrieves all active_tim_holding records from the database. + * 2. Identifies likely stale active_tim_holding records. + * 3. Checks for active_tim records with the same client_id and deletes them. + * 4. Deletes likely stale active_tim_holding records. + * 5. Clears the staleRecords set. + * 6. Adds remaining active_tim_holding records to the staleRecords set. + */ + @Override + public void run() { + try { + log.info("Running..."); + + // Retrieve all active_tim_holding records + List currentRecords = retrieveAllActiveTimHoldingRecords(); + log.info("Retrieved {} active_tim_holding records", currentRecords.size()); + + // If no stale records identified last run, this could be the first time the task runs, so add all active_tim_holding records to staleRecords set + if (staleRecordsIdentifiedLastRun.isEmpty()) { + log.info("No stale records identified last run. Adding {} active_tim_holding records to staleRecords set for next run", currentRecords.size()); + for (ActiveTimHolding record : currentRecords) { + staleRecordsIdentifiedLastRun.add(record.getActiveTimHoldingId()); + } + return; + } + + // Separate likely stale active_tim_holding records + List likelyStaleRecords = new ArrayList<>(); + List newRecords = new ArrayList<>(); + for (ActiveTimHolding record : currentRecords) { + if (staleRecordsIdentifiedLastRun.contains(record.getActiveTimHoldingId())) { + likelyStaleRecords.add(record); + } else { + newRecords.add(record); + } + } + log.info("Identified {} likely stale active_tim_holding records", likelyStaleRecords.size()); + + // Check for active_tim records + List activeTims = retrieveAllActiveTimRecords(); + HashMap> activeTimIdsByClientId = mapActiveTimIdsByClientId(activeTims); + List activeTimIdsToDelete = new ArrayList<>(); + for (ActiveTimHolding record : likelyStaleRecords) { + List activeTimIds = activeTimIdsByClientId.get(record.getClientId()); + if (activeTimIds == null) { + continue; + } + activeTimIdsToDelete.addAll(activeTimIds); + // TODO: Delete only if the failed update was to expire the active TIM. + // TODO: Consider re-submitting the active TIM if the failed update was not meant to expire it. + } + removeActiveTimRecords(activeTimIdsToDelete); // active_tim records are no longer up-to-date + + + // Delete likely stale active_tim_holding records + removeActiveTimHoldingRecords(likelyStaleRecords); + log.info("Deleted {} likely stale active_tim_holding records", likelyStaleRecords.size()); + + // Clear staleRecords set + log.info("Clearing staleRecords set..."); + staleRecordsIdentifiedLastRun.clear(); + + // Record for future runs + for (ActiveTimHolding record : newRecords) { + staleRecordsIdentifiedLastRun.add(record.getActiveTimHoldingId()); + } + log.info("Added {} active_tim_holding records to staleRecords set for next run", newRecords.size()); + } catch (Exception e) { + log.error("Failed to clean up stale active_tim_holding records: {}", e.getMessage(), e); + } + } + + private static HashMap> mapActiveTimIdsByClientId(List activeTims) { + HashMap> activeTimIdsByClientId = new HashMap<>(); + for (ActiveTim activeTim : activeTims) { + if (!activeTimIdsByClientId.containsKey(activeTim.getClientId())) { + activeTimIdsByClientId.put(activeTim.getClientId(), new ArrayList<>()); + } + activeTimIdsByClientId.get(activeTim.getClientId()).add(activeTim.getActiveTimId()); + } + return activeTimIdsByClientId; + } + + private List retrieveAllActiveTimHoldingRecords() throws CleanupStaleActiveTimHoldingRecordsException { + try { + return activeTimHoldingService.getAllRecords(); + } catch (Exception e) { + throw new CleanupStaleActiveTimHoldingRecordsException("Failed to retrieve all active_tim_holding records. Is the cv-data-controller service running?", e); + } + } + + private List retrieveAllActiveTimRecords() throws CleanupStaleActiveTimHoldingRecordsException { + try { + return activeTimService.getAllRecords(); + } catch (Exception e) { + throw new CleanupStaleActiveTimHoldingRecordsException("Failed to retrieve all active_tim records. Is the cv-data-controller service running?", e); + } + } + + private void removeActiveTimRecords(List activeTimIds) throws CleanupStaleActiveTimHoldingRecordsException { + try { + boolean success = activeTimService.deleteActiveTimsById(activeTimIds); + if (!success) { + log.error("Failed to delete active_tim records with ids: {}", activeTimIds); + } else { + log.info("Deleted corresponding active_tim records with ids: ({}), which were outdated (the presence of stale active_tim_holding records indicates a failure to update).", + activeTimIds); + } + } catch (Exception e) { + throw new CleanupStaleActiveTimHoldingRecordsException( + String.format("Failed to delete active_tim record with ids: %s. Is the cv-data-controller service running?", activeTimIds), + e + ); + } + } + + private void removeActiveTimHoldingRecords(List records) throws CleanupStaleActiveTimHoldingRecordsException { + List activeTimHoldingIds = new ArrayList<>(); + for (ActiveTimHolding record : records) { + activeTimHoldingIds.add(record.getActiveTimHoldingId()); + } + try { + boolean success = activeTimHoldingService.deleteActiveTimHoldingRecords(activeTimHoldingIds); + if (!success) { + log.error("Failed to delete active_tim_holding records with ids: {}", activeTimHoldingIds); + } + } catch (Exception e) { + throw new CleanupStaleActiveTimHoldingRecordsException( + String.format("Failed to delete active_tim_holding record with ids: %s. Is the cv-data-controller service running?", + activeTimHoldingIds), + e + ); + } + } + + /** + * For testing purposes, this method returns the staleRecordsIdentifiedLastRun set. + */ + protected Set getStaleRecordsIdentifiedLastRun() { + return staleRecordsIdentifiedLastRun; + } + + /** + * For testing purposes, this method sets the staleRecordsIdentifiedLastRun set. + */ + protected void setStaleRecordsIdentifiedLastRun(Set staleRecordsIdentifiedLastRun) { + this.staleRecordsIdentifiedLastRun.clear(); + this.staleRecordsIdentifiedLastRun.addAll(staleRecordsIdentifiedLastRun); + } + + private static class CleanupStaleActiveTimHoldingRecordsException extends Exception { + public CleanupStaleActiveTimHoldingRecordsException(String message, Throwable cause) { + super(message, cause); + } + } +} \ No newline at end of file diff --git a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/RemoveExpiredActiveTims.java b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/RemoveExpiredActiveTims.java index 39f0ea520..930818b26 100644 --- a/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/RemoveExpiredActiveTims.java +++ b/cv-data-tasks/src/main/java/com/trihydro/tasks/actions/RemoveExpiredActiveTims.java @@ -9,13 +9,16 @@ import com.trihydro.library.service.RestTemplateProvider; import com.trihydro.tasks.config.DataTasksConfiguration; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; +import org.springframework.web.client.ResourceAccessException; +@Slf4j @Component public class RemoveExpiredActiveTims implements Runnable { private DataTasksConfiguration configuration; @@ -32,36 +35,61 @@ public void InjectDependencies(DataTasksConfiguration configuration, Utility _ut restTemplateProvider = _restTemplateProvider; } + /** + * This method is called by the task scheduler to remove expired Active TIMs in batches. + * + * The rationale for processing batches of expired Active TIMs is to avoid connection timeouts + * when deleting a large number of Active TIMs. If we tried to delete all expired Active TIMs + * in one batch, and it took longer than the configured connection timeout, the task would fail + * and the Active TIMs would not be deleted. + */ public void run() { - utility.logWithDate("Running...", this.getClass()); + log.info("Running..."); - try { - // select active tims - List activeTims = activeTimService.getExpiredActiveTims(); - utility.logWithDate("Found " + activeTims.size() + " expired Active TIMs", this.getClass()); + int batchSize = 500; + int maxBatchCount = 50; + int batchCount = 0; + while (true) { + try { + // select active tims + List activeTims = activeTimService.getExpiredActiveTims(batchSize); + log.info("Retrieved a batch of {} expired Active TIMs", activeTims.size()); + if (activeTims.isEmpty()) { + break; + } - // delete active tims from rsus - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity entity = null; - String activeTimJson; - Gson gson = new Gson(); + // delete active tims from rsus and sdx + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = null; + String activeTimJson; + Gson gson = new Gson(); - // send to tim type endpoint to delete from RSUs and SDWs - for (ActiveTim activeTim : activeTims) { + // send to tim type endpoint to delete from RSUs and SDX + for (ActiveTim activeTim : activeTims) { - activeTimJson = gson.toJson(activeTim); - entity = new HttpEntity(activeTimJson, headers); + activeTimJson = gson.toJson(activeTim); + entity = new HttpEntity(activeTimJson, headers); - utility.logWithDate("Deleting ActiveTim: { activeTimId: " + activeTim.getActiveTimId() + " }", - this.getClass()); - restTemplateProvider.GetRestTemplate().exchange(configuration.getWrapperUrl() + "/delete-tim/", - HttpMethod.DELETE, entity, String.class); + log.info("Attempting to delete ActiveTim: { activeTimId: {} }", activeTim.getActiveTimId()); + restTemplateProvider.GetRestTemplate() + .exchange(configuration.getWrapperUrl() + "/delete-tim/", + HttpMethod.DELETE, entity, String.class); + } + } catch (ResourceAccessException e) { + log.error("Error accessing resource. This indicates that the ODE Wrapper or CV Data Controller is not reachable. No more batches will be processed until the next run.", e); + // the error should not be rethrown, or else the task will not run until the service is restarted + break; + } catch (Exception e) { + log.error("Unexpected error occurred while processing expired Active TIMs", e); + // the error should not be rethrown, or else the task will not run until the service is restarted + } + batchCount++; + if (batchCount >= maxBatchCount) { + log.warn("Maximum batches reached. This indicates either A) repeated failures to delete expired Active TIMs or B) a large number of expired Active TIMs (more than {}). No more batches will be processed until the next run.", maxBatchCount * batchSize); + break; } - } catch (Exception e) { - e.printStackTrace(); - // don't rethrow error, or the task won't be reran until the service is - // restarted. } + log.info("{} attempts were made to process batches of expired Active TIMs", batchCount); } } \ No newline at end of file diff --git a/cv-data-tasks/src/main/java/com/trihydro/tasks/config/DataTasksConfiguration.java b/cv-data-tasks/src/main/java/com/trihydro/tasks/config/DataTasksConfiguration.java index 2a8c7ebc1..fc9ed339e 100644 --- a/cv-data-tasks/src/main/java/com/trihydro/tasks/config/DataTasksConfiguration.java +++ b/cv-data-tasks/src/main/java/com/trihydro/tasks/config/DataTasksConfiguration.java @@ -17,8 +17,7 @@ @Component @ConfigurationProperties("config") -public class DataTasksConfiguration implements SdwProps, RsuDataServiceProps, CVRestServiceProps, TmddProps, EmailProps, - TimGenerationProps, OdeProps { +public class DataTasksConfiguration implements SdwProps, RsuDataServiceProps, CVRestServiceProps, TmddProps, EmailProps, TimGenerationProps, OdeProps { private String cvRestService; private String rsuDataServiceUrl; @@ -39,7 +38,7 @@ public class DataTasksConfiguration implements SdwProps, RsuDataServiceProps, CV private int rsuValidationDelaySeconds = 60; private int rsuValThreadPoolSize = 1; private int rsuValTimeoutSeconds = 300; // 76 RSUs, 20s timeout each... Could still finish processing with up to 20% - // of RSUs down in a pool w/ single thread + // of RSUs down in a pool w/ single thread private int removeExpiredPeriodMinutes = 1440; private boolean retention_removeTims = true; @@ -59,6 +58,8 @@ public class DataTasksConfiguration implements SdwProps, RsuDataServiceProps, CV private String[] rsuRoutes; private Double pointIncidentBufferMiles; + private long cleanupStaleActiveTimHoldingRecordsPeriodMinutes = 60; // cleanup stale active tim holding records every hour + /** * Returns the defaultLaneWidth / 2 * @@ -359,4 +360,12 @@ public boolean getRunHsmCheck() { public void setRunHsmCheck(boolean runHsmCheck) { this.runHsmCheck = runHsmCheck; } + + public long getCleanupStaleActiveTimHoldingRecordsPeriodMinutes() { + return cleanupStaleActiveTimHoldingRecordsPeriodMinutes; + } + + public void setCleanupStaleActiveTimHoldingRecordsPeriodMinutes(long cleanupStaleActiveTimHoldingRecordsPeriodMinutes) { + this.cleanupStaleActiveTimHoldingRecordsPeriodMinutes = cleanupStaleActiveTimHoldingRecordsPeriodMinutes; + } } \ No newline at end of file diff --git a/cv-data-tasks/src/main/resources/application-dev.properties b/cv-data-tasks/src/main/resources/application-dev.properties index 5b64a247f..153c3cdd9 100644 --- a/cv-data-tasks/src/main/resources/application-dev.properties +++ b/cv-data-tasks/src/main/resources/application-dev.properties @@ -32,4 +32,5 @@ config.runHsmCheck=true config.sdwTtl=oneyear config.defaultLaneWidth=50 config.rsuRoutes = route1, route2 -config.pointIncidentBufferMiles=1 \ No newline at end of file +config.pointIncidentBufferMiles=1 +config.cleanupStaleActiveTimHoldingRecordsPeriodMinutes=60 \ No newline at end of file diff --git a/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/CleanupStaleActiveTimHoldingRecordsTest.java b/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/CleanupStaleActiveTimHoldingRecordsTest.java new file mode 100644 index 000000000..d38442461 --- /dev/null +++ b/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/CleanupStaleActiveTimHoldingRecordsTest.java @@ -0,0 +1,142 @@ +package com.trihydro.tasks.actions; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import com.trihydro.library.model.ActiveTim; +import com.trihydro.library.model.ActiveTimHolding; +import com.trihydro.library.service.ActiveTimHoldingService; +import com.trihydro.library.service.ActiveTimService; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +class CleanupStaleActiveTimHoldingRecordsTest { + + @Mock + ActiveTimHoldingService activeTimHoldingService; + + @Mock + ActiveTimService activeTimService; + + @BeforeEach + void setUp() { + activeTimHoldingService = mock(ActiveTimHoldingService.class); + activeTimService = mock(ActiveTimService.class); + } + + /** + * Test to verify that the initial run adds all records to the staleRecords set. + */ + @Test + void run_InitialRun_ShouldAddAllRecordsToStaleRecordsSet() { + // prepare + ActiveTimHolding ath1 = new ActiveTimHolding(); + ath1.setActiveTimHoldingId(1L); + ActiveTimHolding ath2 = new ActiveTimHolding(); + ath2.setActiveTimHoldingId(2L); + when(activeTimHoldingService.getAllRecords()).thenReturn(Arrays.asList(ath1, ath2)); + CleanupStaleActiveTimHoldingRecords cleanupStaleActiveTimHoldingRecords = new CleanupStaleActiveTimHoldingRecords(activeTimHoldingService, activeTimService); + + // execute + cleanupStaleActiveTimHoldingRecords.run(); + + // verify + assertEquals(2, cleanupStaleActiveTimHoldingRecords.getStaleRecordsIdentifiedLastRun().size()); + verifyNoInteractions(activeTimService); + } + + /** + * Test to verify that subsequent runs with no matching ActiveTims delete all stale records. + */ + @Test + void run_SubsequentRun_NoMatchingActiveTims_ShouldDeleteAllStaleRecords() { + // prepare + ActiveTimHolding ath1 = new ActiveTimHolding(); + ath1.setActiveTimHoldingId(1L); + ActiveTimHolding ath2 = new ActiveTimHolding(); + ath2.setActiveTimHoldingId(2L); + ActiveTimHolding ath3 = new ActiveTimHolding(); + ath3.setActiveTimHoldingId(3L); + when(activeTimHoldingService.getAllRecords()).thenReturn(Arrays.asList(ath1, ath2, ath3)); + when(activeTimService.getAllRecords()).thenReturn(List.of()); + CleanupStaleActiveTimHoldingRecords cleanupStaleActiveTimHoldingRecords = new CleanupStaleActiveTimHoldingRecords(activeTimHoldingService, activeTimService); + cleanupStaleActiveTimHoldingRecords.setStaleRecordsIdentifiedLastRun(Set.of(1L, 2L)); // set stale records from previous run + + // execute + cleanupStaleActiveTimHoldingRecords.run(); + + // verify + assertEquals(1, cleanupStaleActiveTimHoldingRecords.getStaleRecordsIdentifiedLastRun().size()); + verify(activeTimService).getAllRecords(); + verify(activeTimHoldingService).deleteActiveTimHoldingRecords(List.of(1L, 2L)); + } + + /** + * Test to verify that subsequent runs with matching ActiveTims delete stale records and ActiveTims. + */ + @Test + void run_SubsequentRun_WithMatchingActiveTims_ShouldDeleteStaleRecordsAndActiveTims() { + // prepare + ActiveTimHolding ath1 = new ActiveTimHolding(); + ath1.setActiveTimHoldingId(1L); + ath1.setClientId("test1"); + ActiveTimHolding ath2 = new ActiveTimHolding(); + ath2.setActiveTimHoldingId(2L); + ath2.setClientId("test2"); + when(activeTimHoldingService.getAllRecords()).thenReturn(Arrays.asList(ath1, ath2)); + ActiveTim activeTim = new ActiveTim(); + activeTim.setActiveTimId(37L); + activeTim.setClientId("test1"); + when(activeTimService.getAllRecords()).thenReturn(List.of(activeTim)); + CleanupStaleActiveTimHoldingRecords cleanupStaleActiveTimHoldingRecords = new CleanupStaleActiveTimHoldingRecords(activeTimHoldingService, activeTimService); + cleanupStaleActiveTimHoldingRecords.setStaleRecordsIdentifiedLastRun(Set.of(1L, 2L)); // set stale records from previous run + + // execute + cleanupStaleActiveTimHoldingRecords.run(); + + // verify + assertEquals(0, cleanupStaleActiveTimHoldingRecords.getStaleRecordsIdentifiedLastRun().size()); + verify(activeTimService).getAllRecords(); + verify(activeTimHoldingService).deleteActiveTimHoldingRecords(List.of(1L, 2L)); + verify(activeTimService).deleteActiveTimsById(List.of(37L)); + } + + /** + * Test to verify that the run method handles database connection failures gracefully. + */ + @Test + void run_WhenDatabaseConnectionFails_ShouldHandleGracefully() { + // prepare + when(activeTimHoldingService.getAllRecords()).thenThrow(new RuntimeException()); + CleanupStaleActiveTimHoldingRecords cleanupStaleActiveTimHoldingRecords = new CleanupStaleActiveTimHoldingRecords(activeTimHoldingService, activeTimService); + + // execute + cleanupStaleActiveTimHoldingRecords.run(); + + // verify + verifyNoInteractions(activeTimService); + } + + /** + * Test to verify that the run method handles an empty database gracefully. + */ + @Test + void run_WhenDatabaseIsEmpty_ShouldHandleGracefully() { + // prepare + when(activeTimHoldingService.getAllRecords()).thenReturn(List.of()); + CleanupStaleActiveTimHoldingRecords cleanupStaleActiveTimHoldingRecords = new CleanupStaleActiveTimHoldingRecords(activeTimHoldingService, activeTimService); + + // execute + cleanupStaleActiveTimHoldingRecords.run(); + + // verify + verifyNoInteractions(activeTimService); + } +} \ No newline at end of file diff --git a/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/RemoveExpiredActiveTimsTest.java b/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/RemoveExpiredActiveTimsTest.java index 74431e9b1..37351a033 100644 --- a/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/RemoveExpiredActiveTimsTest.java +++ b/cv-data-tasks/src/test/java/com/trihydro/tasks/actions/RemoveExpiredActiveTimsTest.java @@ -48,7 +48,8 @@ public void setup() { List expiredTims = new ArrayList(); expiredTims.add(new ActiveTim()); expiredTims.add(new ActiveTim()); - when(mockActiveTimService.getExpiredActiveTims()).thenReturn(expiredTims); + when(mockActiveTimService.getExpiredActiveTims(500)).thenReturn(expiredTims).thenReturn( + new ArrayList<>()); } @Test diff --git a/db-scripts/pgsql/mocking/setup-mocks.sh b/db-scripts/pgsql/mocking/setup-mocks.sh old mode 100644 new mode 100755 diff --git a/db-scripts/pgsql/setup/setup.sh b/db-scripts/pgsql/setup/setup.sh old mode 100644 new mode 100755 diff --git a/db-scripts/pgsql/static-data/insert-static-data.sh b/db-scripts/pgsql/static-data/insert-static-data.sh old mode 100644 new mode 100755 diff --git a/db-scripts/pgsql/static-data/sql/03-itis_code.sql b/db-scripts/pgsql/static-data/sql/03-itis_code.sql index 36888b82a..3b238de62 100644 --- a/db-scripts/pgsql/static-data/sql/03-itis_code.sql +++ b/db-scripts/pgsql/static-data/sql/03-itis_code.sql @@ -65,7 +65,6 @@ INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (2 INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (23,E'Snow',2,4868); INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (24,E'Winter storm',2,4871); INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (25,E'Rain',2,4885); -INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (26,E'Strong winds',2,5127); INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (28,E'Visibility reduced',2,5383); INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (29,E'Blowing snow',2,5385); INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (30,E'Black ice',2,5908); @@ -95,5 +94,32 @@ INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (4 INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (415,E'30',1,12574); INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (426,E'10',1,12554); +-- BOWR ITIS CODES (5127, 2563, 2569, 7682, 2577, 8739 and 11589-11607) +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (26,E'Strong winds',2,5127); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (428,E'Truck restriction',2,2563); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (429,E'No high profile vehicles',2,2569); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (430,E'Below',2,7682); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (431,E'Gross-Weight-Limit',2,2577); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (432,E'Pounds',2,8739); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (433,E'20000',1,11589); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (434,E'21000',1,11590); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (435,E'22000',1,11591); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (436,E'23000',1,11592); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (437,E'24000',1,11593); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (438,E'25000',1,11594); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (439,E'26000',1,11595); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (440,E'27000',1,11596); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (441,E'28000',1,11597); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (442,E'29000',1,11598); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (443,E'30000',1,11599); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (444,E'35000',1,11600); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (445,E'40000',1,11601); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (446,E'45000',1,11602); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (447,E'50000',1,11603); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (448,E'55000',1,11604); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (449,E'60000',1,11605); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (450,E'65000',1,11606); +INSERT INTO itis_code (itis_code_id,description,category_id,itis_code) VALUES (451,E'70000',1,11607); + COMMIT; diff --git a/db-scripts/pgsql/teardown/teardown.sh b/db-scripts/pgsql/teardown/teardown.sh old mode 100644 new mode 100755 diff --git a/developer-tools/geojson-from-ode-tim/sampledata.json b/developer-tools/geojson-from-ode-tim/sampledata.json index e6daa0016..b8ec7a2fc 100644 --- a/developer-tools/geojson-from-ode-tim/sampledata.json +++ b/developer-tools/geojson-from-ode-tim/sampledata.json @@ -21,7 +21,7 @@ "packetID": "24BE29D8A92FDF0930", "dataframes": [ { - "notUsed": 0, + "doNotUse1": 0, "frameType": "advisory", "msgId": { "roadSignID": { @@ -36,7 +36,7 @@ "startDateTime": "2024-10-02T11:00:50.814Z", "durationTime": 32000, "priority": 5, - "notUsed1": 0, + "doNotUse2": 0, "regions": [ { "name": "D_US 14_SAT-D99EEE98_RC_BJUS14EB", @@ -443,8 +443,8 @@ } } ], - "notUsed2": 0, - "notUsed3": 0, + "doNotUse3": 0, + "doNotUse4": 0, "content": "advisory", "items": [ "770", diff --git a/docker-compose.yml b/docker-compose.yml index af0a6d10d..f067bf275 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,10 +29,11 @@ services: SERVER_SSL_KEY_STORE_PASSWORD: ${WRAPPER_SERVER_SSL_KEY_STORE_PASSWORD} SERVER_SSL_KEY_STORE_TYPE: ${WRAPPER_SERVER_SSL_KEY_STORE_TYPE} SERVER_SSL_KEY_ALIAS: ${WRAPPER_SERVER_SSL_KEY_ALIAS} + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} ports: - "7777:7777" volumes: - - /home/wyocvadmin/wyocv/ode-wrapper:/keystore + - /home/timmadmin/timm/ode-wrapper:/keystore logging: options: max-size: "20m" @@ -79,6 +80,8 @@ services: CONFIG_DEFAULT_LANE_WIDTH: ${TASKS_CONFIG_DEFAULT_LANE_WIDTH} CONFIG_RSU_ROUTES: ${TASKS_CONFIG_RSU_ROUTES} CONFIG_POINT_INCIDENT_BUFFER_MILES: ${TASKS_CONFIG_POINT_INCIDENT_BUFFER_MILES} + CONFIG_CLEANUP_STALE_ACTIVE_TIM_HOLDING_RECORDS_PERIOD_MINUTES: ${TASKS_CONFIG_CLEANUP_STALE_ACTIVE_TIM_HOLDING_RECORDS_PERIOD_MINUTES} + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" @@ -107,6 +110,7 @@ services: CONFIG_ENVIRONMENT_NAME: ${ENVIRONMENT_NAME} CONFIG_MAIL_HOST: ${MAIL_HOST} CONFIG_MAIL_PORT: ${MAIL_PORT} + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" @@ -136,8 +140,9 @@ services: SERVER_SSL_KEY_STORE_PASSWORD: ${CONTROLLER_SERVER_SSL_KEY_STORE_PASSWORD} SERVER_SSL_KEY_STORE_TYPE: ${CONTROLLER_SERVER_SSL_KEY_STORE_TYPE} SERVER_SSL_KEY_ALIAS: ${CONTROLLER_SERVER_SSL_KEY_ALIAS} + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} volumes: - - /home/wyocvadmin/wyocv/cv-data-controller:/keystore + - /home/timmadmin/timm/cv-data-controller:/keystore logging: options: max-size: "20m" @@ -156,6 +161,7 @@ services: CONFIG_SNMP_AUTH_PASSPHRASE: ${RSUCONTROLLER_CONFIG_SNMP_AUTH_PASSPHRASE} CONFIG_SNMP_AUTH_PROTOCOL: ${RSUCONTROLLER_CONFIG_SNMP_AUTH_PROTOCOL} CONFIG_SNMP_SECURITY_LEVEL: ${RSUCONTROLLER_CONFIG_SNMP_SECURITY_LEVEL} + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" @@ -178,6 +184,7 @@ services: MONGOLOGGER_ENVIRONMENT_NAME: ${ENVIRONMENT_NAME} MONGOLOGGER_MAIL_HOST: ${MAIL_HOST} MONGOLOGGER_MAIL_PORT: ${MAIL_PORT} + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" @@ -200,6 +207,7 @@ services: MONGOLOGGER_ENVIRONMENT_NAME: ${ENVIRONMENT_NAME} MONGOLOGGER_MAIL_HOST: ${MAIL_HOST} MONGOLOGGER_MAIL_PORT: ${MAIL_PORT} + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" @@ -222,6 +230,7 @@ services: MONGOLOGGER_ENVIRONMENT_NAME: ${ENVIRONMENT_NAME} MONGOLOGGER_MAIL_HOST: ${MAIL_HOST} MONGOLOGGER_MAIL_PORT: ${MAIL_PORT} + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" @@ -242,6 +251,7 @@ services: CV_REST_SERVICE: ${LOGGER_CV_REST_SERVICE} PRODUCER_TOPIC: ${DBCONSUMER_DEPOSIT_TOPIC} ENV: ${LOGGER_ENV} + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" @@ -267,25 +277,7 @@ services: MAIL_HOST: ${MAIL_HOST} MAIL_PORT: ${MAIL_PORT} ENV: ${DBCONSUMER_ENV} - logging: - options: - max-size: "20m" - max-file: "3" - - milepost-graph-db: - build: milepost-graph-db - restart: always - ports: - - "6474:7474" - - "6473:7473" - - "6687:7687" - volumes: - - ./milepost-graph-db/neo-data/plugins:/var/lib/neo4j/plugins - - ./milepost-graph-db/neo-data/import:/var/lib/neo4j/import - - ./milepost-graph-db/neo-data/conf:/var/lib/neo4j/conf - - ./milepost-graph-db/neo-data/data:/var/lib/neo4j/data - environment: - NEO4J_AUTH: none + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" @@ -306,6 +298,7 @@ services: DEPOSIT_GROUP: ${EXP_GROUP} CV_REST_SERVICE: ${LOGGER_CV_REST_SERVICE} PRODUCER_TOPIC: ${DBCONSUMER_DEPOSIT_TOPIC} + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" diff --git a/gather_and_compress_jars.sh b/gather_and_compress_jars.sh index 4d9b358ce..c82366d9f 100644 --- a/gather_and_compress_jars.sh +++ b/gather_and_compress_jars.sh @@ -9,7 +9,7 @@ mv ./*/target/*.jar ./jars # compress jars echo "Compressing JARs" date=$(date '+%Y%m%dT%H.%M') -tar -czvf wyocv-jars-$date.tar.gz ./jars +tar -czvf timm-jars-$date.tar.gz ./jars # done echo "Done" diff --git a/images/diagrams/data-flow-diagram.drawio b/images/diagrams/data-flow-diagram.drawio index 61fab4fab..c6668f38f 100644 --- a/images/diagrams/data-flow-diagram.drawio +++ b/images/diagrams/data-flow-diagram.drawio @@ -1,242 +1,245 @@ - + - + - + - - + + - + - - - - - - + + + + + + - - + + - + - - + + - + - - - - - - - + + + + + + + + - - + + - + - - + + - - + + - + - - + + - + - - + + - + - - + + - - + + - - + + - + - - + + - + - - + + - + - - + + - - + + - + - - - - - - + + + + + + - + - - + + - + - - + + - + - - - - - - - - + + + + + + + + - - + + - + - - + + - + - - - - - + + + + + + - - + + - + - - - - - + + + + + + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/images/diagrams/data-flow-diagram.png b/images/diagrams/data-flow-diagram.png index 1eab9c658..0226f267a 100644 Binary files a/images/diagrams/data-flow-diagram.png and b/images/diagrams/data-flow-diagram.png differ diff --git a/load-test/README.md b/load-test/README.md index 94ecc8fc5..d456a5b5b 100644 --- a/load-test/README.md +++ b/load-test/README.md @@ -1,5 +1,5 @@ # Load Testing Resources -This folder contains assets to help us understand how the WyoCV applications perform under load. These tests are executed locally using an open source load testing application, k6. +This folder contains assets to help us understand how the TIMM applications perform under load. These tests are executed locally using an open source load testing application, k6. ## How to use 1. Install k6 ([see here](https://k6.io/docs/getting-started/installation)) 2. View k6 documentation for authoring new test scripts ([see here](https://k6.io/docs/getting-started/running-k6)) diff --git a/local-deployment/README.md b/local-deployment/README.md index d9da5a38e..dac3a4e50 100644 --- a/local-deployment/README.md +++ b/local-deployment/README.md @@ -44,7 +44,7 @@ The postgres database service is defined in the `docker-compose.yml` file, but a ``` ### Preparing JAR Files -The dockerfiles for the wyocv services expect the JAR files to be in the same directory as the Dockerfile. After compilation, copy the JAR files to the appropriate directories. +The dockerfiles for the TIMM services expect the JAR files to be in the same directory as the Dockerfile. After compilation, copy the JAR files to the appropriate directories. ### Certificates The certificate file that is being used must be present in the same directory as the Dockerfile for the `cv-data-tasks` service. diff --git a/local-deployment/docker-compose.yml b/local-deployment/docker-compose.yml index 9ff1b78a0..d33ed2200 100644 --- a/local-deployment/docker-compose.yml +++ b/local-deployment/docker-compose.yml @@ -23,7 +23,7 @@ services: KAFKA_CFG_LOG_RETENTION_HOURS: 2 logging: options: - max-size: "10m" + max-size: "10m" max-file: "5" kafka_init: @@ -33,10 +33,10 @@ services: condition: service_started volumes: - ./docker-scripts/kafka/kafka_init.sh:/kafka_init.sh - entrypoint: ["/bin/sh", "kafka_init.sh"] - + entrypoint: [ "/bin/sh", "kafka_init.sh" ] + ode: - image: usdotjpoode/jpo-ode:2024-q2 + image: usdotjpoode/jpo-ode:2025-q1 ports: - "8080:8080" - "9090:9090" @@ -52,13 +52,28 @@ services: - "6666:6666/udp" environment: DOCKER_HOST_IP: ${DOCKER_HOST_IP} - ZK: ${DOCKER_HOST_IP}:2181 + ODE_KAFKA_BROKERS: ${DOCKER_HOST_IP}:9092 ODE_SECURITY_SVCS_SIGNATURE_URI: http://notused ODE_RSU_USERNAME: testusername ODE_RSU_PASSWORD: testpassword DATA_SIGNING_ENABLED_RSU: false DATA_SIGNING_ENABLED_SDW: false DEFAULT_SNMP_PROTOCOL: NTCIP1218 + KAFKA_TYPE: local + CONFLUENT_KEY: notused + CONFLUENT_SECRET: notused + KAFKA_LINGER_MS: 1 + KAFKA_ACKS: all + KAFKA_RETRIES: 0 + KAFKA_BATCH_SIZE: 16384 + KAFKA_BUFFER_MEMORY: 33554432 + KAFKA_COMPRESSION_TYPE: zstd + KAFKA_KEY_SERIALIZER: org.apache.kafka.common.serialization.StringSerializer + KAFKA_VALUE_SERIALIZER: org.apache.kafka.common.serialization.StringSerializer + KAFKA_PARTITIONER_CLASS: org.apache.kafka.clients.producer.internals.DefaultPartitioner + ODE_TIM_INGEST_MONITORING_ENABLED: false + ODE_TIM_INGEST_MONITORING_INTERVAL: 10000 + depends_on: - kafka volumes: @@ -68,15 +83,15 @@ services: options: max-size: "10m" max-file: "5" - + adm: - image: usdotjpoode/asn1_codec:2024-q2 + image: usdotjpoode/asn1_codec:2025-q1 environment: DOCKER_HOST_IP: ${DOCKER_HOST_IP} ACM_CONFIG_FILE: adm.properties ACM_LOG_TO_CONSOLE: "true" ACM_LOG_TO_FILE: "false" - ACM_LOG_LEVEL: ERROR + ACM_LOG_LEVEL: ${ACM_LOG_LEVEL} depends_on: - kafka volumes: @@ -88,13 +103,13 @@ services: restart: on-failure aem: - image: usdotjpoode/asn1_codec:2024-q2 + image: usdotjpoode/asn1_codec:2025-q1 environment: DOCKER_HOST_IP: ${DOCKER_HOST_IP} ACM_CONFIG_FILE: aem.properties ACM_LOG_TO_CONSOLE: "true" ACM_LOG_TO_FILE: "false" - ACM_LOG_LEVEL: ERROR + ACM_LOG_LEVEL: ${ACM_LOG_LEVEL} depends_on: - kafka volumes: @@ -107,7 +122,7 @@ services: # Note: the SDX does not accept unsigned data, so this is commented out for now # sdw_depositor: - # image: usdotjpoode/jpo-sdw-depositor:2024-q2 + # image: usdotjpoode/jpo-sdw-depositor:2025-q1 # environment: # DOCKER_HOST_IP: ${DOCKER_HOST_IP} # SDW_EMAIL_LIST: test@gmail.com @@ -154,28 +169,7 @@ services: max-size: '10m' # end of external dependencies ---------------------------------------------- - # internal dependencies ------------------------------------------------------ - milepost-graph-db: - build: ../milepost-graph-db - restart: always - ports: - - "6474:7474" - - "6473:7473" - - "6687:7687" - volumes: - - ../milepost-graph-db/neo-data/plugins:/var/lib/neo4j/plugins - - ../milepost-graph-db/neo-data/import:/var/lib/neo4j/import - - ../milepost-graph-db/neo-data/conf:/var/lib/neo4j/conf - - ../milepost-graph-db/neo-data/data:/var/lib/neo4j/data - environment: - NEO4J_AUTH: none - logging: - options: - max-size: "20m" - max-file: "3" - # end of internal dependencies ------------------------------------------------ - - # wyocv apps -------------------------------------------------------------- + # timm apps -------------------------------------------------------------- cv-data-controller: build: ../cv-data-controller restart: always @@ -190,12 +184,13 @@ services: CONFIG_CONNECTION_TIMEOUT: 300000 CONFIG_ODE_URL: http://ode:8080 CONFIG_ENV: local - SPRING_DATA_NEO4J_URI: bolt://milepost-graph-db:7687 + SPRING_DATA_NEO4J_URI: ${NEO4J_URI} CONFIG_ALERT_ADDRESSES: test@gmail.com CONFIG_FROM_EMAIL: test@gmail.com CONFIG_ENVIRONMENT_NAME: local CONFIG_MAIL_HOST: 172.0.0.1 CONFIG_MAIL_PORT: 123 + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" @@ -226,6 +221,7 @@ services: CONFIG_HTTP_LOGGING_MAX_SIZE: 2000 CONFIG_RSU_ROUTES: ${RSU_ROUTES} CONFIG_POINT_INCIDENT_BUFFER_MILES: 1 + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} ports: - "7777:7777" logging: @@ -246,6 +242,7 @@ services: CONFIG_SNMP_AUTH_PASSPHRASE: testpassword CONFIG_SNMP_AUTH_PROTOCOL: SHA CONFIG_SNMP_SECURITY_LEVEL: authNoPriv + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" @@ -292,6 +289,8 @@ services: CONFIG_DEFAULT_LANE_WIDTH: 50 CONFIG_RSU_ROUTES: ${RSU_ROUTES} CONFIG_POINT_INCIDENT_BUFFER_MILES: 1 + CONFIG_CLEANUP_STALE_ACTIVE_TIM_HOLDING_RECORDS_PERIOD_MINUTES: ${TASKS_CONFIG_CLEANUP_STALE_ACTIVE_TIM_HOLDING_RECORDS_PERIOD_MINUTES} + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" @@ -316,6 +315,7 @@ services: CV_REST_SERVICE: http://cv-data-controller:8888 PRODUCER_TOPIC: topic.OracleDataLogger ENV: local + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" @@ -341,6 +341,7 @@ services: MAIL_HOST: 172.0.0.1 MAIL_PORT: 123 ENV: local + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" @@ -358,13 +359,14 @@ services: CONFIG_DEFAULT_LANE_WIDTH: 50 CONFIG_RSU_ROUTES: ${RSU_ROUTES} CONFIG_POINT_INCIDENT_BUFFER_MILES: 1 - CRON_EXPRESSION: "0 0 * * * ?" + CRON_EXPRESSION: ${REFRESH_CRON_EXPRESSION} CONFIG_ENV: local CONFIG_ALERT_ADDRESSES: test@gmail.com CONFIG_FROM_EMAIL: test@gmail.com CONFIG_ENVIRONMENT_NAME: local CONFIG_MAIL_HOST: 172.0.0.1 CONFIG_MAIL_PORT: 123 + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" @@ -384,6 +386,7 @@ services: DEPOSIT_GROUP: certExpirationGroup CV_REST_SERVICE: http://cv-data-controller:8888 PRODUCER_TOPIC: topic.OracleDataLogger + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" @@ -406,6 +409,7 @@ services: MONGOLOGGER_ENVIRONMENT_NAME: local MONGOLOGGER_MAIL_HOST: 172.0.0.1 MONGOLOGGER_MAIL_PORT: 123 + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" @@ -428,6 +432,7 @@ services: MONGOLOGGER_ENVIRONMENT_NAME: local MONGOLOGGER_MAIL_HOST: 172.0.0.1 MONGOLOGGER_MAIL_PORT: 123 + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" @@ -450,16 +455,17 @@ services: MONGOLOGGER_ENVIRONMENT_NAME: local MONGOLOGGER_MAIL_HOST: 172.0.0.1 MONGOLOGGER_MAIL_PORT: 123 + LOGGING_LEVEL_COM_TRIHYDRO: ${LOGGING_LEVEL_COM_TRIHYDRO} logging: options: max-size: "20m" max-file: "3" - # end of wyocv apps --------------------------------------------------------- + # end of timm apps --------------------------------------------------------- # volumes --------------------------------------------------------------------- volumes: kafka: - {} + { } pgdb: driver: local mongodb_data_container: diff --git a/local-deployment/sample.env b/local-deployment/sample.env index dc6bdb6ce..3d2c7497b 100644 --- a/local-deployment/sample.env +++ b/local-deployment/sample.env @@ -8,4 +8,15 @@ POSTGRES_USER=username POSTGRES_PASSWORD=password MONGO_USER=username MONGO_PASSWORD=password -RSU_ROUTES="route1, route2" \ No newline at end of file +RSU_ROUTES="route1, route2" +TASKS_CONFIG_CLEANUP_STALE_ACTIVE_TIM_HOLDING_RECORDS_PERIOD_MINUTES=5 +LOGGING_LEVEL_COM_TRIHYDRO=DEBUG + +# second minute hour day month day-of-week +REFRESH_CRON_EXPRESSION="0 0 1 * * *" + +# URI for the Neo4j database (which is not included in the local deployment) +NEO4J_URI=bolt://${DOCKER_HOST_IP}:6687 + +# log level of asn1_codec instances +ACM_LOG_LEVEL=ERROR \ No newline at end of file diff --git a/logger-kafka-consumer/Dockerfile b/logger-kafka-consumer/Dockerfile index e7a1433e1..123fb5ecc 100644 --- a/logger-kafka-consumer/Dockerfile +++ b/logger-kafka-consumer/Dockerfile @@ -1,5 +1,5 @@ FROM maven:3.8-eclipse-temurin-21-alpine -ADD . /home/wyocv/wyocv_applications/logger-kafka-consumer +ADD . /home/timm/timm_applications/logger-kafka-consumer -CMD java -jar /home/wyocv/wyocv_applications/logger-kafka-consumer/logger-kafka-consumer-1.4.0-SNAPSHOT.jar +CMD java -jar /home/timm/timm_applications/logger-kafka-consumer/logger-kafka-consumer-2.0.0.jar diff --git a/logger-kafka-consumer/README.md b/logger-kafka-consumer/README.md index 846b95d3b..36cc17b43 100644 --- a/logger-kafka-consumer/README.md +++ b/logger-kafka-consumer/README.md @@ -15,7 +15,7 @@ For actions that the consumer can take, see [Actions](#actions). - [Usage](#usage) ## Installation -The following instructions are intended to be executed from the root directory of the WyoCV project: +The following instructions are intended to be executed from the root directory of the TIMM project: 1. Open the project in the provided dev container 1. Compile the project by running the following command: ```bash @@ -46,7 +46,7 @@ The following instructions are intended to be executed from the root directory o ``` ## Deployment -This application is deployed using Docker, and is part of the larger WyoCVApplication suite. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. +This application is deployed using Docker, and is part of the larger TIM Manager. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. ## Configuration The following table describes the configurable environment variables for the module: diff --git a/logger-kafka-consumer/pom.xml b/logger-kafka-consumer/pom.xml index cf3999f6e..811694f23 100644 --- a/logger-kafka-consumer/pom.xml +++ b/logger-kafka-consumer/pom.xml @@ -8,9 +8,9 @@ --> - com.wyocv - wyo-cv - 1.4.0-SNAPSHOT + com.timm + tim-manager + 2.0.0 logger-kafka-consumer diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/Application.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/Application.java index 663cab63c..9a33eab82 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/Application.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/Application.java @@ -1,5 +1,6 @@ package com.trihydro.loggerkafkaconsumer; +import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; import com.trihydro.library.factory.KafkaFactory; import com.trihydro.library.helpers.DbInteractions; import com.trihydro.library.helpers.EmailHelper; @@ -18,7 +19,7 @@ @Import({ Utility.class, DbInteractions.class, JsonToJavaConverter.class, TimDbTables.class, SQLNullHandler.class, EmailHelper.class, JavaMailSenderImplProvider.class, - KafkaFactory.class, RegionNameElementCollection.class }) + KafkaFactory.class, RegionNameElementCollection.class, IdenticalPointsExceptionHandler.class }) @SpringBootApplication @EnableConfigurationProperties(LoggerConfiguration.class) public class Application { diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/LoggerKafkaConsumer.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/LoggerKafkaConsumer.java index b88d0aa94..627195a68 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/LoggerKafkaConsumer.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/LoggerKafkaConsumer.java @@ -1,6 +1,5 @@ package com.trihydro.loggerkafkaconsumer.app; -import java.io.IOException; import java.util.Date; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -18,6 +17,7 @@ import com.trihydro.loggerkafkaconsumer.app.services.TimService; import com.trihydro.loggerkafkaconsumer.config.LoggerConfiguration; +import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.springframework.beans.factory.annotation.Autowired; @@ -26,23 +26,23 @@ import us.dot.its.jpo.ode.model.OdeData; @Component +@Slf4j public class LoggerKafkaConsumer { - private ObjectMapper mapper; - private LoggerConfiguration loggerConfig; - private KafkaFactory kafkaFactory; - private ActiveTimService activeTimService; - private ActiveTimHoldingService activeTimHoldingService; - private TimService timService; - private TimDataConverter timDataConverter; - private Utility utility; - private EmailHelper emailHelper; + private final LoggerConfiguration loggerConfig; + private final KafkaFactory kafkaFactory; + private final ActiveTimService activeTimService; + private final ActiveTimHoldingService activeTimHoldingService; + private final TimService timService; + private final TimDataConverter timDataConverter; + private final Utility utility; + private final EmailHelper emailHelper; @Autowired public LoggerKafkaConsumer(LoggerConfiguration _loggerConfig, KafkaFactory _kafkaFactory, - ActiveTimService _activeTimService, TimService _timService, - TimDataConverter _timDataConverter, Utility _utility, EmailHelper _emailHelper, - ActiveTimHoldingService _activeTimHoldingService) throws IOException, Exception { + ActiveTimService _activeTimService, TimService _timService, + TimDataConverter _timDataConverter, Utility _utility, EmailHelper _emailHelper, + ActiveTimHoldingService _activeTimHoldingService) throws Exception { loggerConfig = _loggerConfig; kafkaFactory = _kafkaFactory; activeTimService = _activeTimService; @@ -52,9 +52,9 @@ public LoggerKafkaConsumer(LoggerConfiguration _loggerConfig, KafkaFactory _kafk emailHelper = _emailHelper; activeTimHoldingService = _activeTimHoldingService; - System.out.println("starting.............."); + log.info("Logger Kafka Consumer starting.............."); - mapper = new ObjectMapper(); + ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); startKafkaConsumer(); } @@ -63,8 +63,8 @@ public void startKafkaConsumer() throws Exception { String endpoint = loggerConfig.getKafkaHostServer() + ":9092"; var stringConsumer = kafkaFactory.createStringConsumer(endpoint, loggerConfig.getDepositGroup(), - loggerConfig.getDepositTopic(), Integer.valueOf(loggerConfig.getMaxPollIntervalMs()), - Integer.valueOf(loggerConfig.getMaxPollRecords())); + loggerConfig.getDepositTopic(), loggerConfig.getMaxPollIntervalMs(), + loggerConfig.getMaxPollRecords()); Gson gson = new Gson(); @@ -75,7 +75,7 @@ public void startKafkaConsumer() throws Exception { ConsumerRecords records = stringConsumer.poll(100); recordCount = records.count(); if (recordCount > 0) { - utility.logWithDate(String.format("Found %d records to parse", recordCount)); + log.info("Found {} records to parse", recordCount); } for (ConsumerRecord record : records) { TopicDataWrapper tdw = null; @@ -83,97 +83,95 @@ public void startKafkaConsumer() throws Exception { tdw = gson.fromJson(record.value(), TopicDataWrapper.class); } catch (Exception e) { // Could be ioException, JsonParseException, JsonMappingException - e.printStackTrace(); + log.error("Failed to parse record: {}", record.value(), e); } if (tdw != null && tdw.getData() != null) { - utility.logWithDate(String.format("Found data for topic: %s", tdw.getTopic())); + log.info("Found data for topic: {}", tdw.getTopic()); switch (tdw.getTopic()) { - case "topic.OdeTimJson": - utility.logWithDate("Before processing JSON: " + tdw.getData()); - odeData = timDataConverter.processTimJson(tdw.getData()); - utility.logWithDate(String.format("Parsed TIM: %s", gson.toJson(odeData))); - if (odeData != null) { - if (odeData.getMetadata() + case "topic.OdeTimJson": + log.trace("Before processing JSON: {}", tdw.getData()); + odeData = timDataConverter.processTimJson(tdw.getData()); + log.trace("After processing JSON: {}", gson.toJson(odeData)); + if (odeData != null) { + if (odeData.getMetadata() .getRecordGeneratedBy() == us.dot.its.jpo.ode.model.OdeMsgMetadata.GeneratedBy.TMC) { - timService.addActiveTimToDatabase(odeData); - } else if (odeData.getMetadata().getRecordGeneratedBy() == null) { - // we shouldn't get here...log it - utility.logWithDate("Failed to get recordGeneratedBy, continuing..."); + timService.addActiveTimToDatabase(odeData); + } else if (odeData.getMetadata().getRecordGeneratedBy() == null) { + // we shouldn't get here...log it + log.error("Failed to get recordGeneratedBy, continuing..."); + } else { + timService.addTimToDatabase(odeData); + } } else { - timService.addTimToDatabase(odeData); + log.error("Failed to parse topic.OdeTimJson, insert fails"); } - } else { - utility.logWithDate("Failed to parse topic.OdeTimJson, insert fails"); - } - break; - - case "topic.OdeTIMCertExpirationTimeJson": - try { - CertExpirationModel certExpirationModel = gson.fromJson(tdw.getData(), + break; + + case "topic.OdeTIMCertExpirationTimeJson": + try { + CertExpirationModel certExpirationModel = gson.fromJson(tdw.getData(), CertExpirationModel.class); - var success = timService.updateActiveTimExpiration(certExpirationModel); - if (success) { - utility.logWithDate("Successfully updated expiration date"); - } else { - // Check for issues - var activeTim = activeTimService + var success = timService.updateActiveTimExpiration(certExpirationModel); + if (success) { + log.info("Successfully updated expiration date"); + } else { + // Check for issues + var activeTim = activeTimService .getActiveTimByPacketId(certExpirationModel.getPacketID()); - // Check if activeTim exists - if (activeTim == null) { - // active_tim not created yet, check active_tim_holding - var ath = activeTimHoldingService + // Check if activeTim exists + if (activeTim == null) { + // active_tim not created yet, check active_tim_holding + var ath = activeTimHoldingService .getActiveTimHoldingByPacketId(certExpirationModel.getPacketID()); - if (ath != null) { - // update ath expiration - success = activeTimHoldingService.updateTimExpiration( + if (ath != null) { + // update ath expiration + success = activeTimHoldingService.updateTimExpiration( certExpirationModel.getPacketID(), certExpirationModel.getExpirationDate()); + } + } else if (messageSuperseded(certExpirationModel.getStartDateTime(), activeTim)) { + // Message superseded + log.info("Unable to update expiration date for Active Tim {} (Packet ID: {}). Message superseded.", + activeTim.getActiveTimId(), certExpirationModel.getPacketID()); } - } else if (messageSuperseded(certExpirationModel.getStartDateTime(), activeTim)) { - // Message superseded - utility.logWithDate(String.format( - "Unable to update expiration date for Active Tim %s (Packet ID: %s). Message superseded.", - activeTim.getActiveTimId(), certExpirationModel.getPacketID())); - } - if (!success) { - // Message either not superseded, or not found in active_tim nor holding tables. error case - utility.logWithDate(String.format("Failed to update expiration for data: %s", - tdw.getData())); + if (!success) { + // Message either not superseded, or not found in active_tim nor holding tables. error case + log.error("Failed to update expiration for data: {}", tdw.getData()); - String body = "logger-kafka-consumer failed attempting to update the expiration for an ActiveTim record"; - body += "
"; - body += "The associated expiration topic record is:
"; - body += tdw.getData(); - emailHelper.SendEmail(loggerConfig.getAlertAddresses(), + String body = "logger-kafka-consumer failed attempting to update the expiration for an ActiveTim record"; + body += "
"; + body += "The associated expiration topic record is:
"; + body += tdw.getData(); + emailHelper.SendEmail(loggerConfig.getAlertAddresses(), "Failed To Update ActiveTim Expiration", body); + } } + } catch (Exception ex) { + log.error("Failed to parse topic.OdeTIMCertExpirationTimeJson, insert fails", ex); } - } catch (Exception ex) { - ex.printStackTrace(); - } - break; + break; } } else { - utility.logWithDate("Logger Kafka Consumer failed to deserialize proper TopicDataWrapper"); + log.error("Logger Kafka Consumer failed to deserialize proper TopicDataWrapper"); if (tdw != null) { - utility.logWithDate(gson.toJson(tdw)); + log.error("Data: {}", tdw.getData()); } } } } } catch (Exception ex) { - utility.logWithDate(ex.getMessage()); + log.error("Error in Kafka Consumer: {}", ex.getMessage()); emailHelper.ContainerRestarted(loggerConfig.getAlertAddresses(), loggerConfig.getMailPort(), - loggerConfig.getMailHost(), loggerConfig.getFromEmail(), "Logger Kafka Consumer"); + loggerConfig.getMailHost(), loggerConfig.getFromEmail(), "Logger Kafka Consumer"); throw ex; } finally { try { stringConsumer.close(); } catch (Exception consumerEx) { - consumerEx.printStackTrace(); + log.error("Failed to close consumer", consumerEx); } } } @@ -193,7 +191,7 @@ private boolean messageSuperseded(String startTime, ActiveTim dbRecord) { // currently processing has been superseded. return expectedStart.getTime() < dbRecord.getStartTimestamp().getTime(); } catch (Exception ex) { - utility.logWithDate("Error while checking if message was superseded: " + ex.getMessage()); + log.error("Error while checking if message was superseded: {}", ex.getMessage()); return false; } } diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimHoldingService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimHoldingService.java index 70f3ea0dc..18069c563 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimHoldingService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimHoldingService.java @@ -9,49 +9,35 @@ import com.trihydro.library.model.ActiveTimHolding; import com.trihydro.library.model.Coordinate; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @Component +@Slf4j public class ActiveTimHoldingService extends BaseService { public ActiveTimHolding getRsuActiveTimHolding(String clientId, String direction, String ipv4Address) { ActiveTimHolding activeTimHolding = null; - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - - try { - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - String query = "select * from active_tim_holding"; - query += " where rsu_target = '" + ipv4Address; - if (clientId != null) { - query += "' and client_id = '" + clientId + "'"; - } else { - query += "' and client_id is null"; - } - query += " and direction = '" + direction + "'"; - rs = statement.executeQuery(query); + String query = "select * from active_tim_holding"; + query += " where rsu_target = '" + ipv4Address; + if (clientId != null) { + query += "' and client_id = '" + clientId + "'"; + } else { + query += "' and client_id is null"; + } + query += " and direction = '" + direction + "'"; + try ( + Connection connection = dbInteractions.getConnectionPool(); + Statement statement = connection.createStatement(); + ResultSet rs = statement.executeQuery(query); + ) { // convert to ActiveTim object activeTimHolding = getSingleActiveTimHoldingFromResultSet(rs); } catch (SQLException e) { - e.printStackTrace(); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } + log.error("SQL Exception while getting RSU ActiveTimHolding with clientId: {}, direction: {}, ipv4Address: {}", + clientId, direction, ipv4Address, e); } return activeTimHolding; @@ -59,42 +45,26 @@ public ActiveTimHolding getRsuActiveTimHolding(String clientId, String direction public ActiveTimHolding getSdxActiveTimHolding(String clientId, String direction, String satRecordId) { ActiveTimHolding activeTimHolding = null; - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - - try { - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - String query = "select * from active_tim_holding"; - query += " where sat_record_id = '" + satRecordId; - if (clientId != null) { - query += "' and client_id = '" + clientId + "'"; - } else { - query += "' and client_id is null"; - } - query += " and direction = '" + direction + "'"; - rs = statement.executeQuery(query); + String query = "select * from active_tim_holding"; + query += " where sat_record_id = '" + satRecordId; + if (clientId != null) { + query += "' and client_id = '" + clientId + "'"; + } else { + query += "' and client_id is null"; + } + query += " and direction = '" + direction + "'"; + try ( + Connection connection = dbInteractions.getConnectionPool(); + Statement statement = connection.createStatement(); + ResultSet rs = statement.executeQuery(query); + ) { // convert to ActiveTim object activeTimHolding = getSingleActiveTimHoldingFromResultSet(rs); } catch (SQLException e) { - e.printStackTrace(); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } + log.error("SQL Exception while getting SDX ActiveTimHolding with clientId: {}, direction: {}, satRecordId: {}", + clientId, direction, satRecordId, e); } return activeTimHolding; @@ -102,35 +72,19 @@ public ActiveTimHolding getSdxActiveTimHolding(String clientId, String direction public ActiveTimHolding getActiveTimHoldingByPacketId(String packetId) { ActiveTimHolding activeTimHolding = null; - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - try { - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - String query = "select * from active_tim_holding"; - query += " where packet_id = '" + packetId + "'"; - rs = statement.executeQuery(query); + String query = "select * from active_tim_holding"; + query += " where packet_id = '" + packetId + "'"; + try ( + Connection connection = dbInteractions.getConnectionPool(); + Statement statement = connection.createStatement(); + ResultSet rs = statement.executeQuery(query); + ) { // convert to ActiveTim object activeTimHolding = getSingleActiveTimHoldingFromResultSet(rs); } catch (SQLException e) { - e.printStackTrace(); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } + log.error("SQL Exception while getting ActiveTimHolding with packetId: {}", packetId, e); } return activeTimHolding; @@ -146,9 +100,9 @@ private ActiveTimHolding getSingleActiveTimHoldingFromResultSet(ResultSet rs) th activeTimHolding.setRsuTargetId(rs.getString("RSU_TARGET")); activeTimHolding.setSatRecordId(rs.getString("SAT_RECORD_ID")); activeTimHolding.setStartPoint( - new Coordinate(rs.getBigDecimal("START_LATITUDE"), rs.getBigDecimal("START_LONGITUDE"))); + new Coordinate(rs.getBigDecimal("START_LATITUDE"), rs.getBigDecimal("START_LONGITUDE"))); activeTimHolding - .setEndPoint(new Coordinate(rs.getBigDecimal("END_LATITUDE"), rs.getBigDecimal("END_LONGITUDE"))); + .setEndPoint(new Coordinate(rs.getBigDecimal("END_LATITUDE"), rs.getBigDecimal("END_LONGITUDE"))); int projectKey = rs.getInt("PROJECT_KEY"); if (!rs.wasNull()) { @@ -167,71 +121,46 @@ public Boolean deleteActiveTimHolding(Long activeTimHoldingId) { } String updateTableSQL = "DELETE FROM ACTIVE_TIM_HOLDING WHERE ACTIVE_TIM_HOLDING_ID = ?"; - Connection connection = null; - PreparedStatement preparedStatement = null; - try { - connection = dbInteractions.getConnectionPool(); - preparedStatement = connection.prepareStatement(updateTableSQL); + try ( + Connection connection = dbInteractions.getConnectionPool(); + PreparedStatement preparedStatement = connection.prepareStatement(updateTableSQL); + ) { preparedStatement.setLong(1, activeTimHoldingId); var success = dbInteractions.updateOrDelete(preparedStatement); if (success) { - utility.logWithDate("Deleted ACTIVE_TIM_HOLDING with ID: " + activeTimHoldingId); + log.info("Deleted ACTIVE_TIM_HOLDING with ID: {}", activeTimHoldingId); } else { - utility.logWithDate("Failed to delete ACTIVE_TIM_HOLDING with ID: " + activeTimHoldingId); + log.error("Failed to delete ACTIVE_TIM_HOLDING with ID: {}", activeTimHoldingId); } return success; } catch (SQLException e) { - e.printStackTrace(); + log.error("SQL Exception while deleting ACTIVE_TIM_HOLDING with ID: {}", activeTimHoldingId, e); return false; - } finally { - try { - // close prepared statement - if (preparedStatement != null) - preparedStatement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } } } public boolean updateTimExpiration(String packetID, String expDate) { - Connection connection = null; - PreparedStatement preparedStatement = null; - boolean success = false; + boolean success; String updateStatement = "UPDATE ACTIVE_TIM_HOLDING SET EXPIRATION_DATE = ? WHERE PACKET_ID = ?"; - try { - connection = dbInteractions.getConnectionPool(); - preparedStatement = connection.prepareStatement(updateStatement); + try ( + Connection connection = dbInteractions.getConnectionPool(); + PreparedStatement preparedStatement = connection.prepareStatement(updateStatement); + ) { preparedStatement.setObject(1, expDate);// expDate comes in as MST from previously called function - // (GetMinExpiration) + // (GetMinExpiration) preparedStatement.setObject(2, packetID); // execute update statement success = dbInteractions.updateOrDelete(preparedStatement); } catch (Exception e) { - e.printStackTrace(); + log.error("Exception while updating ACTIVE_TIM_HOLDING with packetID: {}", packetID, e); return false; - } finally { - try { - // close prepared statement - if (preparedStatement != null) - preparedStatement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } } - utility.logWithDate(String.format( - "Called ActiveTimHolding UpdateTimExpiration with packetID: %s, expDate: %s. Successful: %s", packetID, - expDate, success)); + log.info("Called ActiveTimHolding UpdateTimExpiration with packetID: {}, expDate: {}. Successful: {}", packetID, + expDate, success); return success; } diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimService.java index d24a6cabc..c9f61034b 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/ActiveTimService.java @@ -19,45 +19,42 @@ import com.trihydro.library.model.Coordinate; import com.trihydro.library.tables.TimDbTables; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component +@Slf4j public class ActiveTimService extends BaseService { private TimDbTables timDbTables; private SQLNullHandler sqlNullHandler; - private Calendar UTCCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + private final Calendar UTCCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); @Autowired - public void InjectDependencies(TimDbTables _timDbTables, SQLNullHandler _sqlNullHandler) { + public void InjectDependencies(TimDbTables _timDbTables, SQLNullHandler _sqlNullHandler) { // TODO: use constructor instead of InjectDependencies timDbTables = _timDbTables; sqlNullHandler = _sqlNullHandler; } public Long insertActiveTim(ActiveTim activeTim) { - Connection connection = null; - PreparedStatement preparedStatement = null; + String insertQueryStatement = timDbTables.buildInsertQueryStatement("active_tim", + timDbTables.getActiveTimTable()); - try { - String insertQueryStatement = timDbTables.buildInsertQueryStatement("active_tim", - timDbTables.getActiveTimTable()); - - // get connection - connection = dbInteractions.getConnectionPool(); - - preparedStatement = connection.prepareStatement(insertQueryStatement, new String[] { "active_tim_id" }); + try ( + Connection connection = dbInteractions.getConnectionPool(); + PreparedStatement preparedStatement = connection.prepareStatement(insertQueryStatement, new String[] {"active_tim_id"}); + ) { int fieldNum = 1; for (String col : timDbTables.getActiveTimTable()) { - if (col.equals("TIM_ID")) + if (col.equals("TIM_ID")) { sqlNullHandler.setLongOrNull(preparedStatement, fieldNum, activeTim.getTimId()); - else if (col.equals("DIRECTION")) + } else if (col.equals("DIRECTION")) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, activeTim.getDirection()); - else if (col.equals("TIM_START")) { - utility.logWithDate( - String.format("Converting %s for TIM_START value", activeTim.getStartDateTime())); + } else if (col.equals("TIM_START")) { + log.info("Converting {} for TIM_START value", activeTim.getStartDateTime()); java.util.Date tim_start_date = utility.convertDate(activeTim.getStartDateTime()); Timestamp ts = new Timestamp(tim_start_date.getTime()); sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, ts); @@ -66,8 +63,9 @@ else if (col.equals("TIM_START")) { java.util.Date tim_end_date = utility.convertDate(activeTim.getEndDateTime()); Timestamp ts = new Timestamp(tim_end_date.getTime()); sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, ts); - } else + } else { preparedStatement.setNull(fieldNum, java.sql.Types.TIMESTAMP); + } } else if (col.equals("EXPIRATION_DATE")) { if (activeTim.getExpirationDateTime() != null) { java.util.Date tim_exp_date = utility.convertDate(activeTim.getExpirationDateTime()); @@ -76,35 +74,39 @@ else if (col.equals("TIM_START")) { } else { preparedStatement.setNull(fieldNum, java.sql.Types.TIMESTAMP); } - } else if (col.equals("TIM_TYPE_ID")) + } else if (col.equals("TIM_TYPE_ID")) { sqlNullHandler.setLongOrNull(preparedStatement, fieldNum, activeTim.getTimTypeId()); - else if (col.equals("ROUTE")) + } else if (col.equals("ROUTE")) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, activeTim.getRoute()); - else if (col.equals("CLIENT_ID")) + } else if (col.equals("CLIENT_ID")) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, activeTim.getClientId()); - else if (col.equals("SAT_RECORD_ID")) + } else if (col.equals("SAT_RECORD_ID")) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, activeTim.getSatRecordId()); - else if (col.equals("PK")) + } else if (col.equals("PK")) { sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, activeTim.getPk()); - else if (col.equals("START_LATITUDE")) { + } else if (col.equals("START_LATITUDE")) { BigDecimal start_lat = null; - if (activeTim.getStartPoint() != null) + if (activeTim.getStartPoint() != null) { start_lat = activeTim.getStartPoint().getLatitude(); + } sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, start_lat); } else if (col.equals("START_LONGITUDE")) { BigDecimal start_lon = null; - if (activeTim.getStartPoint() != null) + if (activeTim.getStartPoint() != null) { start_lon = activeTim.getStartPoint().getLongitude(); + } sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, start_lon); } else if (col.equals("END_LATITUDE")) { BigDecimal end_lat = null; - if (activeTim.getEndPoint() != null) + if (activeTim.getEndPoint() != null) { end_lat = activeTim.getEndPoint().getLatitude(); + } sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, end_lat); } else if (col.equals("END_LONGITUDE")) { BigDecimal end_lon = null; - if (activeTim.getEndPoint() != null) + if (activeTim.getEndPoint() != null) { end_lon = activeTim.getEndPoint().getLongitude(); + } sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, end_lon); } else if (col.equals("PROJECT_KEY")) { sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, activeTim.getProjectKey()); @@ -113,24 +115,12 @@ else if (col.equals("START_LATITUDE")) { fieldNum++; } - Long activeTimId = dbInteractions.executeAndLog(preparedStatement, "active tim"); - return activeTimId; + return dbInteractions.executeAndLog(preparedStatement, "active tim"); } catch (SQLException e) { - e.printStackTrace(); - } finally { - try { - // close prepared statement - if (preparedStatement != null) - preparedStatement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } + log.error("Error inserting active_tim", e); } - return Long.valueOf(0); + return 0L; } public boolean updateActiveTim(ActiveTim activeTim) { @@ -138,8 +128,6 @@ public boolean updateActiveTim(ActiveTim activeTim) { boolean activeTimIdResult = false; String updateTableSQL = "UPDATE ACTIVE_TIM SET TIM_ID = ?, START_LATITUDE = ?, START_LONGITUDE = ?, END_LATITUDE = ?,"; updateTableSQL += "END_LONGITUDE = ?, TIM_START = ?, TIM_END = ?, PK = ?, PROJECT_KEY = ? WHERE ACTIVE_TIM_ID = ?"; - Connection connection = null; - PreparedStatement preparedStatement = null; BigDecimal start_lat = null; BigDecimal start_lon = null; @@ -153,9 +141,10 @@ public boolean updateActiveTim(ActiveTim activeTim) { end_lat = activeTim.getEndPoint().getLatitude(); end_lon = activeTim.getEndPoint().getLongitude(); } - try { - connection = dbInteractions.getConnectionPool(); - preparedStatement = connection.prepareStatement(updateTableSQL); + try ( + Connection connection = dbInteractions.getConnectionPool(); + PreparedStatement preparedStatement = connection.prepareStatement(updateTableSQL); + ) { sqlNullHandler.setLongOrNull(preparedStatement, 1, activeTim.getTimId()); sqlNullHandler.setBigDecimalOrNull(preparedStatement, 2, start_lat); sqlNullHandler.setBigDecimalOrNull(preparedStatement, 3, start_lon); @@ -166,9 +155,9 @@ public boolean updateActiveTim(ActiveTim activeTim) { Timestamp ts = new Timestamp(tim_start_date.getTime()); sqlNullHandler.setTimestampOrNull(preparedStatement, 6, ts); - if (activeTim.getEndDateTime() == null) + if (activeTim.getEndDateTime() == null) { preparedStatement.setNull(7, java.sql.Types.TIMESTAMP); - else { + } else { java.util.Date tim_end_date = utility.convertDate(activeTim.getEndDateTime()); Timestamp ts2 = new Timestamp(tim_end_date.getTime()); sqlNullHandler.setTimestampOrNull(preparedStatement, 7, ts2); @@ -178,20 +167,9 @@ public boolean updateActiveTim(ActiveTim activeTim) { sqlNullHandler.setIntegerOrNull(preparedStatement, 9, activeTim.getProjectKey()); sqlNullHandler.setLongOrNull(preparedStatement, 10, activeTim.getActiveTimId()); activeTimIdResult = dbInteractions.updateOrDelete(preparedStatement); - System.out.println("------ Updated active_tim with id: " + activeTim.getActiveTimId() + " --------------"); + log.info("------ Updated active_tim with id: {} --------------", activeTim.getActiveTimId()); } catch (SQLException e) { - e.printStackTrace(); - } finally { - try { - // close prepared statement - if (preparedStatement != null) - preparedStatement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } + log.error("Error updating active_tim with id: {}", activeTim.getActiveTimId(), e); } return activeTimIdResult; @@ -200,18 +178,15 @@ public boolean updateActiveTim(ActiveTim activeTim) { public ActiveTim getActiveSatTim(String satRecordId, String direction) { ActiveTim activeTim = null; - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - try { - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - String query = "select * from active_tim"; - query += " where sat_record_id = '" + satRecordId + "' and active_tim.direction = '" + direction + "'"; - - rs = statement.executeQuery(query); + String query = "select * from active_tim"; + query += " where sat_record_id = '" + satRecordId + "' and active_tim.direction = '" + direction + "'"; + try ( + Connection connection = dbInteractions.getConnectionPool(); + Statement statement = connection.createStatement(); + ResultSet rs = statement.executeQuery(query); + ) { // convert to ActiveTim object while (rs.next()) { activeTim = new ActiveTim(); @@ -243,21 +218,7 @@ public ActiveTim getActiveSatTim(String satRecordId, String direction) { activeTim.setEndPoint(endPoint); } } catch (SQLException e) { - e.printStackTrace(); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } + log.error("Error getting active_sat_tim with satRecordId: {}, direction: {}", satRecordId, direction, e); } return activeTim; @@ -266,27 +227,23 @@ public ActiveTim getActiveSatTim(String satRecordId, String direction) { public ActiveTim getActiveRsuTim(String clientId, String direction, String ipv4Address) { ActiveTim activeTim = null; - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - - try { - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - // - String query = "select distinct atim.ACTIVE_TIM_ID, atim.TIM_ID, atim.SAT_RECORD_ID,"; - query += " atim.CLIENT_ID, atim.DIRECTION, atim.TIM_END, atim.TIM_START,"; - query += " atim.EXPIRATION_DATE, atim.ROUTE, atim.PK,"; - query += " atim.START_LATITUDE, atim.START_LONGITUDE, atim.END_LATITUDE, atim.END_LONGITUDE"; - query += " from active_tim atim"; - query += " inner join tim_rsu on atim.tim_id = tim_rsu.tim_id"; - query += " inner join rsu on tim_rsu.rsu_id = rsu.rsu_id"; - query += " inner join rsu_view on rsu.deviceid = rsu_view.deviceid"; - query += " where sat_record_id is null and ipv4_address = '" + ipv4Address + "' and client_id = '" - + clientId + "' and atim.direction = '" + direction + "'"; - - rs = statement.executeQuery(query); + String query = "select distinct atim.ACTIVE_TIM_ID, atim.TIM_ID, atim.SAT_RECORD_ID,"; + query += " atim.CLIENT_ID, atim.DIRECTION, atim.TIM_END, atim.TIM_START,"; + query += " atim.EXPIRATION_DATE, atim.ROUTE, atim.PK,"; + query += " atim.START_LATITUDE, atim.START_LONGITUDE, atim.END_LATITUDE, atim.END_LONGITUDE"; + query += " from active_tim atim"; + query += " inner join tim_rsu on atim.tim_id = tim_rsu.tim_id"; + query += " inner join rsu on tim_rsu.rsu_id = rsu.rsu_id"; + query += " inner join rsu_view on rsu.deviceid = rsu_view.deviceid"; + query += " where sat_record_id is null and ipv4_address = '" + ipv4Address + "' and client_id = '" + + clientId + "' and atim.direction = '" + direction + "'"; + + try ( + Connection connection = dbInteractions.getConnectionPool(); + Statement statement = connection.createStatement(); + ResultSet rs = statement.executeQuery(query); + ) { // convert to ActiveTim object while (rs.next()) { activeTim = new ActiveTim(); @@ -318,21 +275,8 @@ public ActiveTim getActiveRsuTim(String clientId, String direction, String ipv4A activeTim.setEndPoint(endPoint); } } catch (SQLException e) { - e.printStackTrace(); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } + log.error("Error getting active_rsu_tim with clientId: {}, direction: {}, ipv4Address: {}", clientId, + direction, ipv4Address, e); } return activeTim; @@ -340,18 +284,15 @@ public ActiveTim getActiveRsuTim(String clientId, String direction, String ipv4A public ActiveTim getActiveTimByPacketId(String packetID) { ActiveTim activeTim = null; - Connection connection = null; - Statement statement = null; - ResultSet rs = null; - try { - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - String query = "SELECT ACTIVE_TIM.* FROM ACTIVE_TIM JOIN TIM ON ACTIVE_TIM.TIM_ID = TIM.TIM_ID " - + "WHERE TIM.PACKET_ID = '" + packetID + "'"; - - rs = statement.executeQuery(query); + String query = "SELECT ACTIVE_TIM.* FROM ACTIVE_TIM JOIN TIM ON ACTIVE_TIM.TIM_ID = TIM.TIM_ID " + + "WHERE TIM.PACKET_ID = '" + packetID + "'"; + try ( + Connection connection = dbInteractions.getConnectionPool(); + Statement statement = connection.createStatement(); + ResultSet rs = statement.executeQuery(query); + ) { // convert to ActiveTim object while (rs.next()) { activeTim = new ActiveTim(); @@ -383,30 +324,14 @@ public ActiveTim getActiveTimByPacketId(String packetID) { activeTim.setEndPoint(endPoint); } } catch (SQLException e) { - e.printStackTrace(); - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } + log.error("Error getting active_tim by packetId: {}", packetID, e); } return activeTim; } public boolean updateActiveTimExpiration(String packetID, String expDate) { - Connection connection = null; - PreparedStatement preparedStatement = null; - boolean success = false; + boolean success; String query = "SELECT ACTIVE_TIM_ID FROM ACTIVE_TIM atim"; query += " INNER JOIN TIM ON atim.TIM_ID = TIM.TIM_ID"; @@ -416,88 +341,61 @@ public boolean updateActiveTimExpiration(String packetID, String expDate) { updateStatement += query; updateStatement += ")"; - try { - connection = dbInteractions.getConnectionPool(); - preparedStatement = connection.prepareStatement(updateStatement); + try ( + Connection connection = dbInteractions.getConnectionPool(); + PreparedStatement preparedStatement = connection.prepareStatement(updateStatement); + ) { DateFormat sdf = new SimpleDateFormat("dd-MMM-yy hh.mm.ss.SSS a"); Date dte = sdf.parse(expDate); Timestamp ts = new Timestamp(dte.getTime()); preparedStatement.setTimestamp(1, ts);// expDate comes in as MST from previously called function - // (GetMinExpiration) + // (GetMinExpiration) preparedStatement.setString(2, packetID); // execute update statement success = dbInteractions.updateOrDelete(preparedStatement); } catch (Exception e) { - e.printStackTrace(); + log.error("Error updating active_tim expiration date with packetID: {}, expDate: {}", packetID, expDate, e); return false; - } finally { - try { - // close prepared statement - if (preparedStatement != null) - preparedStatement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } } - utility.logWithDate(String.format("Called UpdateExpiration with packetID: %s, expDate: %s. Successful: %s", - packetID, expDate, success)); + log.info("Called UpdateExpiration with packetID: {}, expDate: {}. Successful: {}", packetID, expDate, success); return success; } public String getMinExpiration(String packetID, String expDate) throws ParseException { - Connection connection = null; - Statement statement = null; - ResultSet rs = null; + String targetFormat = "DD-MON-YYYY HH12.MI.SS a"; + String selectTimestamp = String.format("SELECT TO_TIMESTAMP('%s', '%s')", + translateIso8601ToTimestampFormat(expDate), targetFormat); + + String minExpDate = "SELECT MIN(EXPIRATION_DATE) FROM ACTIVE_TIM atim"; + minExpDate += " INNER JOIN TIM ON atim.TIM_ID = TIM.TIM_ID"; + minExpDate += " WHERE TIM.PACKET_ID = '" + packetID + "'"; + + String query = String.format("SELECT LEAST((%s), (COALESCE((%s),(%s)))) minStart", + selectTimestamp, minExpDate, selectTimestamp); String minStart = ""; - try { + try ( + Connection connection = dbInteractions.getConnectionPool(); + Statement statement = connection.createStatement(); + ResultSet rs = statement.executeQuery(query); + ) { // Fetch the minimum of passed in expDate and database held // active_tim.expiration_date. To compare like values we convert the expDate // TO_TIMESTAMP. Without this it compares string length. // Also, there are some null values in the db. To get around these, we use the // coalesce function with the expDate passed in value. - connection = dbInteractions.getConnectionPool(); - statement = connection.createStatement(); - String targetFormat = "DD-MON-YYYY HH12.MI.SS a"; - String selectTimestamp = String.format("SELECT TO_TIMESTAMP('%s', '%s')", - translateIso8601ToTimestampFormat(expDate), targetFormat); - - String minExpDate = "SELECT MIN(EXPIRATION_DATE) FROM ACTIVE_TIM atim"; - minExpDate += " INNER JOIN TIM ON atim.TIM_ID = TIM.TIM_ID"; - minExpDate += " WHERE TIM.PACKET_ID = '" + packetID + "'"; - - String query = String.format("SELECT LEAST((%s), (COALESCE((%s),(%s)))) minStart", - selectTimestamp, minExpDate, selectTimestamp); - rs = statement.executeQuery(query); while (rs.next()) { var tmpTs = rs.getTimestamp("MINSTART", UTCCalendar); minStart = utility.timestampFormat.format(tmpTs); } } catch (SQLException e) { - e.printStackTrace(); + log.error("Error getting min expiration date with packetID: {}, expDate: {}", packetID, expDate, e); return null; - } finally { - try { - // close prepared statement - if (statement != null) - statement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - // close result set - if (rs != null) - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } } - utility.logWithDate(String.format("Called GetMinExpiration with packetID: %s, expDate: %s. Min start date: %s", - packetID, expDate, minStart)); + log.info("Called GetMinExpiration with packetID: {}, expDate: {}. Min start date: {}", packetID, expDate, + minStart); return minStart; } diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/DataFrameService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/DataFrameService.java index 0a8e34c14..0506ac69c 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/DataFrameService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/DataFrameService.java @@ -10,43 +10,43 @@ import java.util.Date; import java.util.TimeZone; -import com.trihydro.library.helpers.SQLNullHandler; -import com.trihydro.library.tables.TimDbTables; - +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import com.trihydro.library.helpers.SQLNullHandler; +import com.trihydro.library.tables.TimDbTables; + import us.dot.its.jpo.ode.plugin.j2735.OdeTravelerInformationMessage.DataFrame; @Component +@Slf4j public class DataFrameService extends BaseService { private TimDbTables timDbTables; private SQLNullHandler sqlNullHandler; @Autowired - public void InjectDependencies(TimDbTables _timDbTables, SQLNullHandler _sqlNullHandler) { + public void InjectDependencies(TimDbTables _timDbTables, SQLNullHandler _sqlNullHandler) { // TODO: use constructor instead of InjectDependencies timDbTables = _timDbTables; sqlNullHandler = _sqlNullHandler; } public Long AddDataFrame(DataFrame dFrame, Long timId) { - Connection connection = null; - PreparedStatement preparedStatement = null; - - try { + String insertQueryStatement = timDbTables.buildInsertQueryStatement("data_frame", + timDbTables.getDataFrameTable()); - connection = dbInteractions.getConnectionPool(); - String insertQueryStatement = timDbTables.buildInsertQueryStatement("data_frame", - timDbTables.getDataFrameTable()); - preparedStatement = connection.prepareStatement(insertQueryStatement, new String[] { "data_frame_id" }); + try ( + Connection connection = dbInteractions.getConnectionPool(); + PreparedStatement preparedStatement = connection.prepareStatement(insertQueryStatement, new String[] {"data_frame_id"}); + ) { int fieldNum = 1; for (String col : timDbTables.getDataFrameTable()) { if (col.equals("TIM_ID")) { sqlNullHandler.setLongOrNull(preparedStatement, fieldNum, timId); } else if (col.equals("SSP_TIM_RIGHTS")) { - sqlNullHandler.setShortOrNull(preparedStatement, fieldNum, dFrame.getNotUsed()); + sqlNullHandler.setShortOrNull(preparedStatement, fieldNum, dFrame.getDoNotUse1()); } else if (col.equals("FRAME_TYPE")) { Integer ordinal = null; if (dFrame.getFrameType() != null) { @@ -58,11 +58,11 @@ public Long AddDataFrame(DataFrame dFrame, Long timId) { } else if (col.equals("PRIORITY")) { sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, dFrame.getPriority()); } else if (col.equals("SSP_LOCATION_RIGHTS")) { - sqlNullHandler.setShortOrNull(preparedStatement, fieldNum, dFrame.getNotUsed1()); + sqlNullHandler.setShortOrNull(preparedStatement, fieldNum, dFrame.getDoNotUse2()); } else if (col.equals("SSP_MSG_TYPES")) { - sqlNullHandler.setShortOrNull(preparedStatement, fieldNum, dFrame.getNotUsed3()); + sqlNullHandler.setShortOrNull(preparedStatement, fieldNum, dFrame.getDoNotUse4()); } else if (col.equals("SSP_MSG_CONTENT")) { - sqlNullHandler.setShortOrNull(preparedStatement, fieldNum, dFrame.getNotUsed2()); + sqlNullHandler.setShortOrNull(preparedStatement, fieldNum, dFrame.getDoNotUse3()); } else if (col.equals("CONTENT")) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, dFrame.getContent()); } else if (col.equals("URL")) { @@ -70,18 +70,17 @@ public Long AddDataFrame(DataFrame dFrame, Long timId) { } else if (col.equals("START_DATE_TIME")) { if (dFrame.getStartDateTime() == null) { preparedStatement.setNull(fieldNum, java.sql.Types.TIMESTAMP); - } - else { + } else { Timestamp time = null; try { TimeZone tz = TimeZone.getTimeZone("UTC"); DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); // Quoted "Z" to indicate UTC, no - // timezone offset + // timezone offset df.setTimeZone(tz); Date dt = df.parse(dFrame.getStartDateTime()); time = new Timestamp(dt.getTime()); } catch (ParseException ex) { - utility.logWithDate("Unable to parse startdate: " + dFrame.getStartDateTime()); + log.error("Unable to parse startdate: {}", dFrame.getStartDateTime()); } sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, time); } @@ -89,23 +88,11 @@ public Long AddDataFrame(DataFrame dFrame, Long timId) { fieldNum++; } - Long dataFrameId = dbInteractions.executeAndLog(preparedStatement, "dataframe"); - return dataFrameId; + return dbInteractions.executeAndLog(preparedStatement, "dataframe"); } catch (SQLException e) { - e.printStackTrace(); - } finally { - try { - // close prepared statement - if (preparedStatement != null) - preparedStatement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } + log.error("Error adding data frame", e); } - return Long.valueOf(0); + return 0L; } } \ No newline at end of file diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/NodeLLService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/NodeLLService.java index 93d8f05ca..37d49acff 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/NodeLLService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/NodeLLService.java @@ -45,19 +45,19 @@ else if (col.equals("NODE_LAT")) else if (col.equals("NODE_LONG")) sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, nodeXY.getNodeLong()); else if (col.equals("X")) - sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, nodeXY.getX()); + sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, nodeXY.getXpos()); else if (col.equals("Y")) - sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, nodeXY.getY()); + sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, nodeXY.getYpos()); else if (col.equals("ATTRIBUTES_DWIDTH")) if (nodeXY.getAttributes() != null) sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, - nodeXY.getAttributes().getdWidth()); + nodeXY.getAttributes().getDwidth()); else preparedStatement.setNull(fieldNum, java.sql.Types.NUMERIC); else if (col.equals("ATTRIBUTES_DELEVATION")) if (nodeXY.getAttributes() != null) sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, - nodeXY.getAttributes().getdElevation()); + nodeXY.getAttributes().getDelevation()); else preparedStatement.setNull(fieldNum, java.sql.Types.NUMERIC); fieldNum++; diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/NodeXYService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/NodeXYService.java index 5409d9c66..c522ac062 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/NodeXYService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/NodeXYService.java @@ -45,19 +45,19 @@ else if (col.equals("NODE_LAT")) else if (col.equals("NODE_LONG")) sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, nodeXY.getNodeLong()); else if (col.equals("X")) - sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, nodeXY.getX()); + sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, nodeXY.getXpos()); else if (col.equals("Y")) - sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, nodeXY.getY()); + sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, nodeXY.getYpos()); else if (col.equals("ATTRIBUTES_DWIDTH")) if (nodeXY.getAttributes() != null) sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, - nodeXY.getAttributes().getdWidth()); + nodeXY.getAttributes().getDwidth()); else preparedStatement.setNull(fieldNum, java.sql.Types.NUMERIC); else if (col.equals("ATTRIBUTES_DELEVATION")) if (nodeXY.getAttributes() != null) sqlNullHandler.setBigDecimalOrNull(preparedStatement, fieldNum, - nodeXY.getAttributes().getdElevation()); + nodeXY.getAttributes().getDelevation()); else preparedStatement.setNull(fieldNum, java.sql.Types.NUMERIC); fieldNum++; diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/TimRsuService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/TimRsuService.java index 74c65753e..c0b36a859 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/TimRsuService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/TimRsuService.java @@ -1,69 +1,75 @@ package com.trihydro.loggerkafkaconsumer.app.services; +import com.trihydro.library.helpers.DbInteractions; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; -import java.sql.SQLIntegrityConstraintViolationException; import com.trihydro.library.helpers.SQLNullHandler; import com.trihydro.library.tables.TimDbTables; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +/** + * Service class for handling TIM RSU operations. + */ @Component -public class TimRsuService extends BaseService { - - private TimDbTables timDbTables; - private SQLNullHandler sqlNullHandler; +@Slf4j +public class TimRsuService { + private final DbInteractions dbInteractions; + private final TimDbTables timDbTables; + private final SQLNullHandler sqlNullHandler; + /** + * Constructor for TimRsuService. + * + * @param dbInteractions the database interactions helper + * @param timDbTables the TIM database tables helper + * @param sqlNullHandler the SQL null handler + */ @Autowired - public void InjectDependencies(TimDbTables _timDbTables, SQLNullHandler _sqlNullHandler) { - timDbTables = _timDbTables; - sqlNullHandler = _sqlNullHandler; + public TimRsuService(DbInteractions dbInteractions, TimDbTables timDbTables, SQLNullHandler sqlNullHandler) { + this.dbInteractions = dbInteractions; + this.timDbTables = timDbTables; + this.sqlNullHandler = sqlNullHandler; } + /** + * Adds a TIM RSU record to the database. + * + * @param timId the TIM ID + * @param rsuId the RSU ID + * @param rsuIndex the RSU index + * @return the ID of the inserted TIM RSU record, 0 if a duplicate record is detected, or -1 if an error occurs + */ public Long AddTimRsu(Long timId, Integer rsuId, Integer rsuIndex) { + String insertQueryStatement = timDbTables.buildInsertQueryStatement("tim_rsu", timDbTables.getTimRsuTable()); - PreparedStatement preparedStatement = null; - Connection connection = null; - - try { - connection = dbInteractions.getConnectionPool(); - String insertQueryStatement = timDbTables.buildInsertQueryStatement("tim_rsu", - timDbTables.getTimRsuTable()); - preparedStatement = connection.prepareStatement(insertQueryStatement, new String[] { "tim_rsu_id" }); + try (Connection connection = dbInteractions.getConnectionPool(); PreparedStatement preparedStatement = connection.prepareStatement(insertQueryStatement, new String[] {"tim_rsu_id"})) { int fieldNum = 1; for (String col : timDbTables.getTimRsuTable()) { - if (col.equals("TIM_ID")) + if (col.equals("TIM_ID")) { sqlNullHandler.setLongOrNull(preparedStatement, fieldNum, timId); - else if (col.equals("RSU_ID")) + } else if (col.equals("RSU_ID")) { sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, rsuId); - else if (col.equals("RSU_INDEX")) + } else if (col.equals("RSU_INDEX")) { sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, rsuIndex); + } fieldNum++; } - Long timRsuId = dbInteractions.executeAndLog(preparedStatement, "tim rsu"); - return timRsuId; + return dbInteractions.executeAndLog(preparedStatement, "tim rsu"); } catch (SQLException e) { - // java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (CVCOMMS.TIM_RSU_U) violated - if(!(e instanceof SQLIntegrityConstraintViolationException)) { - e.printStackTrace(); + if (e.getMessage() != null && e.getMessage().contains("unique constraint")) { + // Avoid logging the common error when trying to insert a duplicate record + return 0L; } - return Long.valueOf(0); - } finally { - try { - // close prepared statement - if (preparedStatement != null) - preparedStatement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } + // Log the exception if it's not a unique constraint violation + log.error("SQL Exception while adding TIM RSU: {}", e.getMessage(), e); + return -1L; } } } \ No newline at end of file diff --git a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/TimService.java b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/TimService.java index 8ae48d686..fc006b924 100644 --- a/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/TimService.java +++ b/logger-kafka-consumer/src/main/java/com/trihydro/loggerkafkaconsumer/app/services/TimService.java @@ -9,7 +9,6 @@ import java.time.LocalDateTime; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import java.time.temporal.ChronoUnit; import com.google.gson.Gson; import com.trihydro.library.helpers.SQLNullHandler; @@ -24,6 +23,7 @@ import com.trihydro.library.model.WydotRsu; import com.trihydro.library.tables.TimDbTables; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -44,6 +44,7 @@ import us.dot.its.jpo.ode.plugin.j2735.OdeTravelerInformationMessage.DataFrame.Region.Path; @Component +@Slf4j public class TimService extends BaseService { public Gson gson = new Gson(); @@ -67,12 +68,13 @@ public class TimService extends BaseService { @Autowired public void InjectDependencies(ActiveTimService _ats, TimDbTables _timDbTables, - SQLNullHandler _sqlNullHandler, PathService _pathService, RegionService _regionService, - DataFrameService _dataFrameService, RsuService _rsuService, TimTypeService _tts, - ItisCodeService _itisCodesService, TimRsuService _timRsuService, - DataFrameItisCodeService _dataFrameItisCodeService, PathNodeXYService _pathNodeXYService, - NodeXYService _nodeXYService, Utility _utility, ActiveTimHoldingService _athService, - PathNodeLLService _pathNodeLLService, NodeLLService _nodeLLService) { + SQLNullHandler _sqlNullHandler, PathService _pathService, RegionService _regionService, + DataFrameService _dataFrameService, RsuService _rsuService, TimTypeService _tts, + ItisCodeService _itisCodesService, TimRsuService _timRsuService, + DataFrameItisCodeService _dataFrameItisCodeService, PathNodeXYService _pathNodeXYService, + NodeXYService _nodeXYService, Utility _utility, ActiveTimHoldingService _athService, + PathNodeLLService _pathNodeLLService, + NodeLLService _nodeLLService) { // TODO: use constructor instead of InjectDependencies activeTimService = _ats; timDbTables = _timDbTables; sqlNullHandler = _sqlNullHandler; @@ -95,8 +97,7 @@ public void InjectDependencies(ActiveTimService _ats, TimDbTables _timDbTables, public void addTimToDatabase(OdeData odeData) { try { - - utility.logWithDate("Called addTimToDatabase"); + log.info("Called addTimToDatabase"); ReceivedMessageDetails rxMsgDet = null; RecordType recType = null; @@ -111,7 +112,7 @@ public void addTimToDatabase(OdeData odeData) { } Long timId = AddTim(odeData.getMetadata(), rxMsgDet, getTim((OdeTimPayload) odeData.getPayload()), - recType, logFileName, secResCode, null, null); + recType, logFileName, secResCode, null, null); // return if TIM is not inserted if (timId == null) { @@ -120,7 +121,7 @@ public void addTimToDatabase(OdeData odeData) { DataFrame[] dFrames = getTim((OdeTimPayload) odeData.getPayload()).getDataframes(); if (dFrames.length == 0) { - utility.logWithDate("addTimToDatabase - No dataframes found in TIM (tim_id: " + timId + ")"); + log.info("addTimToDatabase - No dataframes found in TIM (tim_id: {})", timId); return; } OdeTravelerInformationMessage.DataFrame firstDataFrame = dFrames[0]; @@ -135,18 +136,15 @@ public void addTimToDatabase(OdeData odeData) { // if this is an RSU TIM if (activeTim != null && activeTim.getRsuTarget() != null) { // save TIM RSU in DB - WydotRsu rsu = rsuService.getRsus().stream() - .filter(x -> x.getRsuTarget().equals(activeTim.getRsuTarget())).findFirst() - .orElse(null); - if (rsu != null) { - timRsuService.AddTimRsu(timId, rsu.getRsuId(), rsu.getRsuIndex()); - } + rsuService.getRsus().stream() + .filter(x -> x.getRsuTarget().equals(activeTim.getRsuTarget())).findFirst() + .ifPresent(rsu -> timRsuService.AddTimRsu(timId, rsu.getRsuId(), rsu.getRsuIndex())); } addDataFrameItis(firstDataFrame, dataFrameId); } catch (NullPointerException e) { - System.out.println("Null pointer exception encountered in TimService.addTimToDatabase() method: " + e.getMessage()); + log.info("Null pointer exception encountered in TimService.addTimToDatabase() method: {}", e.getMessage()); } } @@ -154,34 +152,40 @@ public void addTimToDatabase(OdeData odeData) { * Adds an active TIM to the database. This only handles a single TIM at a time. */ public void addActiveTimToDatabase(OdeData odeData) { - - utility.logWithDate("Called addActiveTimToDatabase", TimService.class); + log.info("Called addActiveTimToDatabase"); ActiveTim activeTim; OdeTimPayload payload = (OdeTimPayload) odeData.getPayload(); - if (payload == null) + if (payload == null) { return; + } OdeTravelerInformationMessage tim = getTim(payload); - if (tim == null) + if (tim == null) { return; + } DataFrame[] dframes = tim.getDataframes(); - if (dframes == null || dframes.length == 0) + if (dframes == null || dframes.length == 0) { return; + } OdeTravelerInformationMessage.DataFrame.Region[] regions = dframes[0].getRegions(); - if (regions == null || regions.length == 0) + if (regions == null || regions.length == 0) { return; + } String firstRegionName = regions[0].getName(); - if (StringUtils.isEmpty(firstRegionName) || StringUtils.isBlank(firstRegionName)) + if (StringUtils.isEmpty(firstRegionName) || StringUtils.isBlank(firstRegionName)) { return; + } OdeRequestMsgMetadata metaData = (OdeRequestMsgMetadata) odeData.getMetadata(); - if (metaData == null) + if (metaData == null) { return; + } // get information from the region name, first check splitname length activeTim = setActiveTimByRegionName(firstRegionName); - if (activeTim == null) + if (activeTim == null) { return; + } String satRecordId = activeTim.getSatRecordId(); @@ -203,31 +207,30 @@ public void addActiveTimToDatabase(OdeData odeData) { addDataFrameItis(dframes[0], dataFrameId); } else { // failed to insert new tim and failed to fetch existing, log and return - utility.logWithDate( - "Failed to insert tim, and failed to fetch existing tim. No data inserted for OdeData: " - + gson.toJson(odeData)); + log.info("Failed to insert tim, and failed to fetch existing tim. No data inserted for OdeData: {}", gson.toJson(odeData)); return; } } else { - utility.logWithDate("TIM already exists, tim_id " + timId); + log.info("TIM already exists, tim_id {}", timId); } // ensure we handle a new satRecordId - if (satRecordId != null && satRecordId != "") { + if (satRecordId != null && !satRecordId.isEmpty()) { updateTimSatRecordId(timId, satRecordId); - utility.logWithDate("Added sat_record_id of " + satRecordId + " to TIM with tim_id " + timId); + log.info("Added sat_record_id of {} to TIM with tim_id {}", satRecordId, timId); } // TODO : Change to loop through RSU array - doing one rsu for now RSU firstRsu = null; if (metaData.getRequest() != null && metaData.getRequest().getRsus() != null - && metaData.getRequest().getRsus().length > 0) { + && metaData.getRequest().getRsus().length > 0) { firstRsu = metaData.getRequest().getRsus()[0]; activeTim.setRsuTarget(firstRsu.getRsuTarget()); } - if (metaData.getRequest() != null && metaData.getRequest().getSdw() != null) + if (metaData.getRequest() != null && metaData.getRequest().getSdw() != null) { activeTim.setSatRecordId(metaData.getRequest().getSdw().getRecordId()); + } // the ODE now parses all dataframes to find the most recent and sets it // to this new OdeTimStartDateTime. We'll take advantage. @@ -236,8 +239,7 @@ public void addActiveTimToDatabase(OdeData odeData) { var stDate = metaData.getOdeTimStartDateTime(); if (StringUtils.isEmpty(stDate)) { stDate = dframes[0].getStartDateTime(); - utility.logWithDate(String.format( - "addActiveTimToDatabase did not find odeTimStartDateTime, setting to dataframe value %s", stDate)); + log.info("addActiveTimToDatabase did not find odeTimStartDateTime, setting to dataframe value {}", stDate); } activeTim.setStartDateTime(stDate); activeTim.setTimId(timId); @@ -248,34 +250,32 @@ public void addActiveTimToDatabase(OdeData odeData) { if (activeTim.getRsuTarget() != null && firstRsu != null) { // save TIM RSU in DB WydotRsu rsu = rsuService.getRsus().stream().filter(x -> x.getRsuTarget().equals(activeTim.getRsuTarget())) - .findFirst().orElse(null); + .findFirst().orElse(null); if (rsu != null) { timRsuService.AddTimRsu(timId, rsu.getRsuId(), rsu.getRsuIndex()); } ath = activeTimHoldingService.getRsuActiveTimHolding(activeTim.getClientId(), activeTim.getDirection(), - activeTim.getRsuTarget()); + activeTim.getRsuTarget()); if (ath == null) { - utility.logWithDate(String.format( - "Could not find active_tim_holding for client_id '%s', direction '%s', rsu_target '%s'", - activeTim.getClientId(), activeTim.getDirection(), activeTim.getRsuTarget())); + log.info("Could not find active_tim_holding for client_id '{}', direction '{}', rsu_target '{}'", + activeTim.getClientId(), activeTim.getDirection(), activeTim.getRsuTarget()); } } else { // SDX tim, fetch holding ath = activeTimHoldingService.getSdxActiveTimHolding(activeTim.getClientId(), activeTim.getDirection(), - activeTim.getSatRecordId()); + activeTim.getSatRecordId()); if (ath == null) { - utility.logWithDate(String.format( - "Could not find active_tim_holding for client_id '%s', direction '%s', sat_record_id '%s'", - activeTim.getClientId(), activeTim.getDirection(), activeTim.getSatRecordId())); + log.info("Could not find active_tim_holding for client_id '{}', direction '{}', sat_record_id '{}'", + activeTim.getClientId(), activeTim.getDirection(), activeTim.getSatRecordId()); } } // set end time if duration is not indefinite if (dframes[0].getDurationTime() != 32000) { ZonedDateTime zdt = ZonedDateTime.parse(dframes[0].getStartDateTime()); - zdt = zdt.plus(dframes[0].getDurationTime(), ChronoUnit.MINUTES); + zdt = zdt.plusMinutes(dframes[0].getDurationTime()); activeTim.setEndDateTime(zdt.toString()); } @@ -300,10 +300,13 @@ public void addActiveTimToDatabase(OdeData odeData) { // if RSU TIM if (activeTim.getRsuTarget() != null) // look for active RSU tim that matches incoming TIM + { activeTimDb = activeTimService.getActiveRsuTim(activeTim.getClientId(), activeTim.getDirection(), - activeTim.getRsuTarget()); - else // else look for active SAT tim that matches incoming TIM + activeTim.getRsuTarget()); + } else // else look for active SAT tim that matches incoming TIM + { activeTimDb = activeTimService.getActiveSatTim(activeTim.getSatRecordId(), activeTim.getDirection()); + } // if there is no active TIM, insert new one if (activeTimDb == null) { @@ -324,7 +327,7 @@ public void addActiveTimToDatabase(OdeData odeData) { } else { // not from WYDOT application // just log for now - utility.logWithDate("Inserting new active_tim, no TimType found - not from WYDOT application"); + log.info("Inserting new active_tim, no TimType found - not from WYDOT application"); activeTimService.insertActiveTim(activeTim); } @@ -335,83 +338,73 @@ public void addActiveTimToDatabase(OdeData odeData) { } public Long getTimId(String packetId, Timestamp timeStamp) { - ResultSet rs = null; - Connection connection = null; - PreparedStatement preparedStatement = null; Long id = null; - try { - connection = dbInteractions.getConnectionPool(); - preparedStatement = connection - .prepareStatement("select tim_id from tim where packet_id = ? and time_stamp = ?"); + try ( + Connection connection = dbInteractions.getConnectionPool(); + PreparedStatement preparedStatement = connection + .prepareStatement("select tim_id from tim where packet_id = ? and time_stamp = ?"); + ) { preparedStatement.setString(1, packetId); preparedStatement.setTimestamp(2, timeStamp); - rs = preparedStatement.executeQuery(); - if (rs.next()) { - id = rs.getLong("tim_id"); + try ( + ResultSet rs = preparedStatement.executeQuery(); + ) { + if (rs.next()) { + id = rs.getLong("tim_id"); + } } } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - if (preparedStatement != null) - preparedStatement.close(); - - if (connection != null) - connection.close(); - } catch (SQLException ex) { - ex.printStackTrace(); - } + log.error("Failed to get tim_id from database", e); } return id; } public Long AddTim(OdeMsgMetadata odeTimMetadata, ReceivedMessageDetails receivedMessageDetails, - OdeTravelerInformationMessage j2735TravelerInformationMessage, RecordType recordType, String logFileName, - SecurityResultCode securityResultCode, String satRecordId, String regionName) { - PreparedStatement preparedStatement = null; - Connection connection = null; - - try { - - String insertQueryStatement = timDbTables.buildInsertQueryStatement("tim", - timDbTables.getTimTable()); - connection = dbInteractions.getConnectionPool(); - preparedStatement = connection.prepareStatement(insertQueryStatement, new String[] { "tim_id" }); + OdeTravelerInformationMessage j2735TravelerInformationMessage, RecordType recordType, String logFileName, + SecurityResultCode securityResultCode, String satRecordId, String regionName) { + String insertQueryStatement = timDbTables.buildInsertQueryStatement("tim", + timDbTables.getTimTable()); + + try ( + Connection connection = dbInteractions.getConnectionPool(); + PreparedStatement preparedStatement = connection.prepareStatement(insertQueryStatement, new String[] {"tim_id"}); + ) { int fieldNum = 1; for (String col : timDbTables.getTimTable()) { // default to null preparedStatement.setString(fieldNum, null); if (j2735TravelerInformationMessage != null) { - if (col.equals("MSG_CNT")) + if (col.equals("MSG_CNT")) { sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, - j2735TravelerInformationMessage.getMsgCnt()); - else if (col.equals("PACKET_ID")) + j2735TravelerInformationMessage.getMsgCnt()); + } else if (col.equals("PACKET_ID")) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, - j2735TravelerInformationMessage.getPacketID()); - else if (col.equals("URL_B")) + j2735TravelerInformationMessage.getPacketID()); + } else if (col.equals("URL_B")) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, - j2735TravelerInformationMessage.getUrlB()); - else if (col.equals("TIME_STAMP")) { + j2735TravelerInformationMessage.getUrlB()); + } else if (col.equals("TIME_STAMP")) { String timeStamp = j2735TravelerInformationMessage.getTimeStamp(); java.sql.Timestamp ts = null; if (StringUtils.isNotEmpty(timeStamp) && StringUtils.isNotBlank(timeStamp)) { ts = java.sql.Timestamp - .valueOf(LocalDateTime.parse(timeStamp, DateTimeFormatter.ISO_DATE_TIME)); + .valueOf(LocalDateTime.parse(timeStamp, DateTimeFormatter.ISO_DATE_TIME)); } sqlNullHandler.setTimestampOrNull(preparedStatement, fieldNum, ts); } } if (odeTimMetadata != null) { if (col.equals("RECORD_GENERATED_BY")) { - if (odeTimMetadata.getRecordGeneratedBy() != null) + if (odeTimMetadata.getRecordGeneratedBy() != null) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, - odeTimMetadata.getRecordGeneratedBy().toString()); - else + odeTimMetadata.getRecordGeneratedBy().toString()); + } else { preparedStatement.setString(fieldNum, null); + } } else if (col.equals("RECORD_GENERATED_AT")) { if (odeTimMetadata.getRecordGeneratedAt() != null) { java.util.Date recordGeneratedAtDate = utility.convertDate(odeTimMetadata.getRecordGeneratedAt()); @@ -423,10 +416,11 @@ else if (col.equals("TIME_STAMP")) { } else if (col.equals("SCHEMA_VERSION")) { sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, odeTimMetadata.getSchemaVersion()); } else if (col.equals("SANITIZED")) { - if (odeTimMetadata.isSanitized()) + if (odeTimMetadata.isSanitized()) { preparedStatement.setInt(fieldNum, 1); - else + } else { preparedStatement.setInt(fieldNum, 0); + } } else if (col.equals("PAYLOAD_TYPE")) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, odeTimMetadata.getPayloadType()); } else if (col.equals("ODE_RECEIVED_AT")) { @@ -440,68 +434,66 @@ else if (col.equals("TIME_STAMP")) { } if (odeTimMetadata.getSerialId() != null) { - if (col.equals("SERIAL_ID_STREAM_ID")) + if (col.equals("SERIAL_ID_STREAM_ID")) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, - odeTimMetadata.getSerialId().getStreamId()); - else if (col.equals("SERIAL_ID_BUNDLE_SIZE")) + odeTimMetadata.getSerialId().getStreamId()); + } else if (col.equals("SERIAL_ID_BUNDLE_SIZE")) { sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, - odeTimMetadata.getSerialId().getBundleSize()); - else if (col.equals("SERIAL_ID_BUNDLE_ID")) + odeTimMetadata.getSerialId().getBundleSize()); + } else if (col.equals("SERIAL_ID_BUNDLE_ID")) { sqlNullHandler.setLongOrNull(preparedStatement, fieldNum, - odeTimMetadata.getSerialId().getBundleId()); - else if (col.equals("SERIAL_ID_RECORD_ID")) + odeTimMetadata.getSerialId().getBundleId()); + } else if (col.equals("SERIAL_ID_RECORD_ID")) { sqlNullHandler.setIntegerOrNull(preparedStatement, fieldNum, - odeTimMetadata.getSerialId().getRecordId()); - else if (col.equals("SERIAL_ID_SERIAL_NUMBER")) + odeTimMetadata.getSerialId().getRecordId()); + } else if (col.equals("SERIAL_ID_SERIAL_NUMBER")) { sqlNullHandler.setLongOrNull(preparedStatement, fieldNum, - odeTimMetadata.getSerialId().getSerialNumber()); + odeTimMetadata.getSerialId().getSerialNumber()); + } } } if (receivedMessageDetails != null) { if (receivedMessageDetails.getLocationData() != null) { if (col.equals("RMD_LD_ELEVATION")) { sqlNullHandler.setDoubleOrNull(preparedStatement, fieldNum, - Double.parseDouble(receivedMessageDetails.getLocationData().getElevation())); + Double.parseDouble(receivedMessageDetails.getLocationData().getElevation())); } else if (col.equals("RMD_LD_HEADING")) { sqlNullHandler.setDoubleOrNull(preparedStatement, fieldNum, - Double.parseDouble(receivedMessageDetails.getLocationData().getHeading())); + Double.parseDouble(receivedMessageDetails.getLocationData().getHeading())); } else if (col.equals("RMD_LD_LATITUDE")) { sqlNullHandler.setDoubleOrNull(preparedStatement, fieldNum, - Double.parseDouble(receivedMessageDetails.getLocationData().getLatitude())); + Double.parseDouble(receivedMessageDetails.getLocationData().getLatitude())); } else if (col.equals("RMD_LD_LONGITUDE")) { sqlNullHandler.setDoubleOrNull(preparedStatement, fieldNum, - Double.parseDouble(receivedMessageDetails.getLocationData().getLongitude())); + Double.parseDouble(receivedMessageDetails.getLocationData().getLongitude())); } else if (col.equals("RMD_LD_SPEED")) { sqlNullHandler.setDoubleOrNull(preparedStatement, fieldNum, - Double.parseDouble(receivedMessageDetails.getLocationData().getSpeed())); + Double.parseDouble(receivedMessageDetails.getLocationData().getSpeed())); } - } - else { + } else { // location data is null, set all to null (with correct type) if (col.equals("RMD_LD_ELEVATION") || col.equals("RMD_LD_HEADING") || col.equals("RMD_LD_LATITUDE") - || col.equals("RMD_LD_LONGITUDE") || col.equals("RMD_LD_SPEED")) { + || col.equals("RMD_LD_LONGITUDE") || col.equals("RMD_LD_SPEED")) { preparedStatement.setNull(fieldNum, java.sql.Types.NUMERIC); } } if (col.equals("RMD_RX_SOURCE") && receivedMessageDetails.getRxSource() != null) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, - receivedMessageDetails.getRxSource().toString()); + receivedMessageDetails.getRxSource().toString()); } else if (col.equals("SECURITY_RESULT_CODE")) { SecurityResultCodeType securityResultCodeType = GetSecurityResultCodeTypes().stream() - .filter(x -> x.getSecurityResultCodeType().equals(securityResultCode.toString())) - .findFirst().orElse(null); + .filter(x -> x.getSecurityResultCodeType().equals(securityResultCode.toString())) + .findFirst().orElse(null); if (securityResultCodeType != null) { preparedStatement.setInt(fieldNum, securityResultCodeType.getSecurityResultCodeTypeId()); - } - else { + } else { preparedStatement.setNull(fieldNum, java.sql.Types.INTEGER); } } - } - else { + } else { // message details are null, set all to null (with correct type) if (col.equals("RMD_LD_ELEVATION") || col.equals("RMD_LD_HEADING") || col.equals("RMD_LD_LATITUDE") - || col.equals("RMD_LD_LONGITUDE") || col.equals("RMD_LD_SPEED")) { + || col.equals("RMD_LD_LONGITUDE") || col.equals("RMD_LD_SPEED")) { preparedStatement.setNull(fieldNum, java.sql.Types.NUMERIC); } else if (col.equals("RMD_RX_SOURCE")) { preparedStatement.setString(fieldNum, null); @@ -510,11 +502,11 @@ else if (col.equals("SERIAL_ID_SERIAL_NUMBER")) } } - if (col.equals("SAT_RECORD_ID")) + if (col.equals("SAT_RECORD_ID")) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, satRecordId); - else if (col.equals("TIM_NAME")) + } else if (col.equals("TIM_NAME")) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, regionName); - else if (col.equals("LOG_FILE_NAME")) { + } else if (col.equals("LOG_FILE_NAME")) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, logFileName); } else if (col.equals("RECORD_TYPE") && recordType != null) { sqlNullHandler.setStringOrNull(preparedStatement, fieldNum, recordType.toString()); @@ -522,29 +514,17 @@ else if (col.equals("LOG_FILE_NAME")) { fieldNum++; } // execute insert statement - Long timId = dbInteractions.executeAndLog(preparedStatement, "timID"); - return timId; + return dbInteractions.executeAndLog(preparedStatement, "timID"); } catch (SQLException e) { - e.printStackTrace(); - } finally { - try { - // close prepared statement - if (preparedStatement != null) - preparedStatement.close(); - // return connection back to pool - if (connection != null) - connection.close(); - } catch (SQLException e) { - e.printStackTrace(); - } + log.error("Failed to insert tim into database", e); } - return Long.valueOf(0); + return 0L; } /** * Adds regions to the database for a given DataFrame. - * - * @param dataFrame The DataFrame containing the regions to be added. + * + * @param dataFrame The DataFrame containing the regions to be added. * @param dataFrameId The ID of the DataFrame. */ public void addRegions(DataFrame dataFrame, Long dataFrameId) { @@ -571,18 +551,17 @@ public void addRegions(DataFrame dataFrame, Long dataFrameId) { } else if (geometry != null) { regionService.AddRegion(dataFrameId, null, region); } else { - utility.logWithDate( - "addActiveTimToDatabase - Unable to insert region, no path or geometry found (data_frame_id: " - + dataFrameId + ")"); + log.warn("addActiveTimToDatabase - Unable to insert region, no path or geometry found (data_frame_id: {})", + dataFrameId); } - } + } } public void addDataFrameItis(DataFrame dataFrame, Long dataFrameId) { // save DataFrame ITIS codes String[] items = dataFrame.getItems(); if (items == null || items.length == 0) { - System.out.println("No itis codes found to associate with data_frame " + dataFrameId); + log.warn("No itis codes found to associate with data_frame {}", dataFrameId); return; } for (var i = 0; i < items.length; i++) { @@ -592,9 +571,8 @@ public void addDataFrameItis(DataFrame dataFrame, Long dataFrameId) { String itisCodeId = getItisCodeId(timItisCode); if (itisCodeId != null) { dataFrameItisCodeService.insertDataFrameItisCode(dataFrameId, itisCodeId, i); - } - else { - utility.logWithDate("Could not find corresponding itis code it for " + timItisCode); + } else { + log.warn("Could not find corresponding itis code it for {}", timItisCode); } } else { dataFrameItisCodeService.insertDataFrameItisCode(dataFrameId, timItisCode, i); @@ -603,27 +581,15 @@ public void addDataFrameItis(DataFrame dataFrame, Long dataFrameId) { } public boolean updateTimSatRecordId(Long timId, String satRecordId) { - PreparedStatement preparedStatement = null; - Connection connection = null; - - try { - connection = dbInteractions.getConnectionPool(); - preparedStatement = connection.prepareStatement("update tim set sat_record_id = ? where tim_id = ?"); + try ( + Connection connection = dbInteractions.getConnectionPool(); + PreparedStatement preparedStatement = connection.prepareStatement("update tim set sat_record_id = ? where tim_id = ?"); + ) { preparedStatement.setString(1, satRecordId); preparedStatement.setLong(2, timId); return dbInteractions.updateOrDelete(preparedStatement); } catch (Exception ex) { return false; - } finally { - try { - if (preparedStatement != null) - preparedStatement.close(); - - if (connection != null) - connection.close(); - } catch (Exception e) { - e.printStackTrace(); - } } } @@ -640,8 +606,7 @@ public ActiveTim setActiveTimByRegionName(String regionName) { if (elements.route != null) { activeTim.setRoute(elements.route); - } - else { + } else { return activeTim; } if (elements.rsuOrSat != null) { @@ -654,8 +619,9 @@ public ActiveTim setActiveTimByRegionName(String regionName) { activeTim.setRsuTarget(hyphen_array[1]); } } - } else + } else { return activeTim; + } if (elements.timType != null) { TimType timType = getTimType(elements.timType); if (timType != null) { @@ -668,8 +634,7 @@ public ActiveTim setActiveTimByRegionName(String regionName) { if (elements.timId != null) { activeTim.setClientId(elements.timId); - } - else { + } else { return activeTim; } @@ -687,10 +652,8 @@ public ActiveTim setActiveTimByRegionName(String regionName) { public TimType getTimType(String timTypeName) { - TimType timType = timTypeService.getTimTypes().stream().filter(x -> x.getType().equals(timTypeName)).findFirst() - .orElse(null); - - return timType; + return timTypeService.getTimTypes().stream().filter(x -> x.getType().equals(timTypeName)).findFirst() + .orElse(null); } public String getItisCodeId(String item) { @@ -699,12 +662,13 @@ public String getItisCodeId(String item) { try { ItisCode itisCode = itisCodeService.selectAllItisCodes().stream() - .filter(x -> x.getItisCode().equals(Integer.parseInt(item))).findFirst().orElse(null); - if (itisCode != null) + .filter(x -> x.getItisCode().equals(Integer.parseInt(item))).findFirst().orElse(null); + if (itisCode != null) { itisCodeId = itisCode.getItisCodeId().toString(); + } } catch (Exception ex) { // on rare occasions we see an unparsable Integer - utility.logWithDate("Failed to parse ITIS integer(" + item + "): " + ex.getMessage()); + log.error("Failed to parse ITIS integer({}): {}", item, ex.getMessage()); } return itisCodeId; diff --git a/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/dataConverters/TimDataConverterTest.java b/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/dataConverters/TimDataConverterTest.java index 2600838b3..79405f92f 100644 --- a/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/dataConverters/TimDataConverterTest.java +++ b/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/dataConverters/TimDataConverterTest.java @@ -208,19 +208,6 @@ public void processTimJson_odeTimWithRsus() throws IOException { } - @Test - public void processTimJson_unsigned() throws IOException { - // Arrange - String value = new String(Files.readAllBytes(Paths.get("src/test/resources/TIM_unsigned.json"))); - - // Act - var data = uut.processTimJson(value); - - // Assert - Assertions.assertNotNull(data); - Assertions.assertNotNull(data.getMetadata()); - } - /** * Helper method to get an OdeTravelerInformationMessage object given an OdeTimPayload. */ diff --git a/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/DataFrameServiceTest.java b/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/DataFrameServiceTest.java index 658483209..663951b0d 100644 --- a/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/DataFrameServiceTest.java +++ b/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/DataFrameServiceTest.java @@ -1,8 +1,5 @@ package com.trihydro.loggerkafkaconsumer.app.services; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.verify; - import java.sql.SQLException; import java.sql.Timestamp; import java.text.DateFormat; @@ -11,15 +8,17 @@ import java.util.Date; import java.util.TimeZone; -import com.trihydro.library.helpers.SQLNullHandler; -import com.trihydro.library.tables.TimDbTables; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; import org.mockito.Spy; +import com.trihydro.library.helpers.SQLNullHandler; +import com.trihydro.library.tables.TimDbTables; + import us.dot.its.jpo.ode.plugin.j2735.OdeTravelerInformationMessage.DataFrame; public class DataFrameServiceTest extends TestBase { @@ -51,13 +50,13 @@ public void AddDataFrame_SUCCESS() throws SQLException, ParseException { // Assert Assertions.assertEquals(Long.valueOf(-1), data); verify(mockSqlNullHandler).setLongOrNull(mockPreparedStatement, 1, -1l);// TIM_ID - verify(mockSqlNullHandler).setShortOrNull(mockPreparedStatement, 2, dFrame.getNotUsed());// SSP_TIM_RIGHTS + verify(mockSqlNullHandler).setShortOrNull(mockPreparedStatement, 2, dFrame.getDoNotUse1());// SSP_TIM_RIGHTS & NotUsed verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 3, null);// FRAME_TYPE verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 4, dFrame.getDurationTime());// DURATION_TIME verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 5, dFrame.getPriority());// PRIORITY - verify(mockSqlNullHandler).setShortOrNull(mockPreparedStatement, 6, dFrame.getNotUsed1());// SSP_LOCATION_RIGHTS - verify(mockSqlNullHandler).setShortOrNull(mockPreparedStatement, 7, dFrame.getNotUsed3());// SSP_MSG_TYPES - verify(mockSqlNullHandler).setShortOrNull(mockPreparedStatement, 8, dFrame.getNotUsed2());// SSP_MSG_CONTENT + verify(mockSqlNullHandler).setShortOrNull(mockPreparedStatement, 6, dFrame.getDoNotUse2());// SSP_LOCATION_RIGHTS & NotUsed1 + verify(mockSqlNullHandler).setShortOrNull(mockPreparedStatement, 7, dFrame.getDoNotUse4());// SSP_MSG_TYPES & NotUsed3 + verify(mockSqlNullHandler).setShortOrNull(mockPreparedStatement, 8, dFrame.getDoNotUse3());// SSP_MSG_CONTENT & NotUsed2 verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 9, dFrame.getContent());// CONTENT verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 10, dFrame.getUrl());// URL verify(mockSqlNullHandler).setTimestampOrNull(mockPreparedStatement, 11, time);// START_DATE_TIME diff --git a/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/NodeXYServiceTest.java b/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/NodeXYServiceTest.java index 1efffcc5b..7e901a3ee 100644 --- a/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/NodeXYServiceTest.java +++ b/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/NodeXYServiceTest.java @@ -43,11 +43,11 @@ public void AddNodeXY_SUCCESS() throws SQLException { verify(mockSqlNullHandler).setStringOrNull(mockPreparedStatement, 1, nodeXY.getDelta());// DELTA verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 2, nodeXY.getNodeLat());// NODE_LAT verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 3, nodeXY.getNodeLong());// NODE_LONG - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 4, nodeXY.getX());// X - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 5, nodeXY.getY());// Y - verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 6, nodeXY.getAttributes().getdWidth());// ATTRIBUTES_DWIDTH + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 4, nodeXY.getXpos());// X + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 5, nodeXY.getYpos());// Y + verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 6, nodeXY.getAttributes().getDwidth());// ATTRIBUTES_DWIDTH verify(mockSqlNullHandler).setBigDecimalOrNull(mockPreparedStatement, 7, - nodeXY.getAttributes().getdElevation());// ATTRIBUTES_DELEVATION + nodeXY.getAttributes().getDelevation());// ATTRIBUTES_DELEVATION verify(mockPreparedStatement).close(); verify(mockConnection).close(); } diff --git a/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/TimRsuServiceTest.java b/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/TimRsuServiceTest.java index 5c34ff927..cc7cc6d01 100644 --- a/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/TimRsuServiceTest.java +++ b/logger-kafka-consumer/src/test/java/com/trihydro/loggerkafkaconsumer/app/services/TimRsuServiceTest.java @@ -1,61 +1,108 @@ package com.trihydro.loggerkafkaconsumer.app.services; -import java.sql.SQLException; +import com.trihydro.library.helpers.DbInteractions; +import com.trihydro.library.helpers.SQLNullHandler; +import com.trihydro.library.tables.TimDbTables; -import org.junit.jupiter.api.Assertions; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.verify; -import org.mockito.Spy; -import com.trihydro.library.helpers.SQLNullHandler; -import com.trihydro.library.tables.TimDbTables; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; -public class TimRsuServiceTest extends TestBase { +import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; - @Spy - private TimDbTables mockTimDbTables = new TimDbTables(); +public class TimRsuServiceTest { + + @Mock + private DbInteractions mockDbInteractions; + @Mock + private TimDbTables mockTimDbTables; @Mock private SQLNullHandler mockSqlNullHandler; + @Mock + private Connection mockConnection; + @Mock + private PreparedStatement mockPreparedStatement; + + private TimRsuService uut; @BeforeEach - public void setupSubTest() { - uut.InjectDependencies(mockTimDbTables, mockSqlNullHandler); + public void setup() throws Exception { + // Initialize mocks + mockDbInteractions = mock(DbInteractions.class); + mockTimDbTables = mock(TimDbTables.class); + mockSqlNullHandler = mock(SQLNullHandler.class); + mockConnection = mock(Connection.class); + mockPreparedStatement = mock(PreparedStatement.class); + + // Stub common behaviors + when(mockDbInteractions.getConnectionPool()).thenReturn(mockConnection); + when(mockConnection.prepareStatement(anyString(), any(String[].class))).thenReturn(mockPreparedStatement); + when(mockTimDbTables.buildInsertQueryStatement(anyString(), anyList())).thenReturn("INSERT INTO tim_rsu ..."); + when(mockTimDbTables.getTimRsuTable()).thenReturn(List.of("TIM_ID", "RSU_ID", "RSU_INDEX")); + + // Instantiate the service with the mocked dependencies + uut = new TimRsuService(mockDbInteractions, mockTimDbTables, mockSqlNullHandler); } @Test public void AddTimRsu_SUCCESS() throws SQLException { // Arrange - Long timId = -1l; + Long timId = -1L; int rsuId = -2; int rsuIndex = 0; + + when(mockDbInteractions.executeAndLog(any(PreparedStatement.class), anyString())).thenReturn(37L); + // Act Long data = uut.AddTimRsu(timId, rsuId, rsuIndex); // Assert - Assertions.assertEquals(Long.valueOf(-1), data); - verify(mockSqlNullHandler).setLongOrNull(mockPreparedStatement, 1, timId);// TIM_ID - verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 2, rsuId);// RSU_ID - verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 3, rsuIndex);// RSU_INDEX + assertEquals(Long.valueOf(37L), data); + verify(mockSqlNullHandler).setLongOrNull(mockPreparedStatement, 1, timId); // TIM_ID + verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 2, rsuId); // RSU_ID + verify(mockSqlNullHandler).setIntegerOrNull(mockPreparedStatement, 3, rsuIndex); // RSU_INDEX verify(mockPreparedStatement).close(); verify(mockConnection).close(); } @Test - public void AddTimRsu_FAIL() throws SQLException { + public void AddTimRsu_FAIL_UniqueConstraint() throws SQLException { // Arrange - Long timId = -1l; + Long timId = -1L; int rsuId = -2; int rsuIndex = 0; - doThrow(new SQLException("UNIQUENESS CONSTRAINT VIOLATION")).when(mockSqlNullHandler).setLongOrNull(mockPreparedStatement, 1, timId); + + doThrow(new SQLException("duplicate key value violates unique constraint \"tim_rsu_rsu_id_tim_id_rsu_index_key\"")).when(mockSqlNullHandler).setLongOrNull(mockPreparedStatement, 1, timId); + + // Act + Long data = uut.AddTimRsu(timId, rsuId, rsuIndex); + + // Assert + assertEquals(Long.valueOf(0), data); + verify(mockPreparedStatement).close(); + verify(mockConnection).close(); + } + + @Test + public void AddTimRsu_FAIL_OtherSQLException() throws SQLException { + // Arrange + Long timId = -1L; + int rsuId = -2; + int rsuIndex = 0; + + doThrow(new SQLException("other sql exception")).when(mockSqlNullHandler).setLongOrNull(mockPreparedStatement, 1, timId); // Act Long data = uut.AddTimRsu(timId, rsuId, rsuIndex); // Assert - Assertions.assertEquals(Long.valueOf(0), data); + assertEquals(Long.valueOf(-1), data); verify(mockPreparedStatement).close(); verify(mockConnection).close(); } diff --git a/logger-kafka-consumer/src/test/resources/TIM_odeTimStartDateTime.json b/logger-kafka-consumer/src/test/resources/TIM_odeTimStartDateTime.json index 9821975ea..4b66fec03 100644 --- a/logger-kafka-consumer/src/test/resources/TIM_odeTimStartDateTime.json +++ b/logger-kafka-consumer/src/test/resources/TIM_odeTimStartDateTime.json @@ -1,294 +1,309 @@ { - "metadata": { - "request": { - "ode": { - "verb": "POST", - "version": 3 - }, - "sdw": { - "recordId": "047D5D92", - "serviceRegion": { - "nwCorner": { - "latitude": 42.0423655, - "longitude": -110.65625591 - }, - "seCorner": { - "latitude": 41.80934153, - "longitude": -110.53481503 - } - }, - "ttl": "oneyear" - } + "metadata": { + "request": { + "ode": { + "verb": "POST", + "version": 3 + }, + "sdw": { + "recordId": "047D5D92", + "serviceRegion": { + "nwCorner": { + "latitude": 42.0423655, + "longitude": -110.65625591 + }, + "seCorner": { + "latitude": 41.80934153, + "longitude": -110.53481503 + } }, - "recordGeneratedBy": "TMC", - "schemaVersion": 6, - "payloadType": "us.dot.its.jpo.ode.model.OdeTimPayload", - "odePacketID": "1D6FCDA23022F235E6", - "serialId": { - "recordId": 0, - "serialNumber": 39887, - "streamId": "9a4b7448-d26a-41f9-a80d-97f3ac8c1dd7", - "bundleSize": 1, - "bundleId": 39887 - }, - "sanitized": false, - "recordGeneratedAt": "2020-11-11T07:34:05.151191Z", - "maxDurationTime": 32000, - "odeTimStartDateTime": "2020-11-11T07:36:53.035Z", - "odeReceivedAt": "2020-11-11T07:34:05.220Z" + "ttl": "oneyear" + } + }, + "recordGeneratedBy": "TMC", + "schemaVersion": 6, + "payloadType": "us.dot.its.jpo.ode.model.OdeTimPayload", + "odePacketID": "1D6FCDA23022F235E6", + "serialId": { + "recordId": 0, + "serialNumber": 39887, + "streamId": "9a4b7448-d26a-41f9-a80d-97f3ac8c1dd7", + "bundleSize": 1, + "bundleId": 39887 }, - "payload": { - "data": { - "MessageFrame": { - "messageId": 31, - "value": { - "TravelerInformation": { - "timeStamp": 454054, - "packetID": "1D6FCDA23022F235E6", - "urlB": null, - "dataFrames": { - "TravelerDataFrame": { - "regions": { - "GeographicalPath": { - "closedPath": { - "false": "" - }, - "anchor": { - "lat": 418082518, - "long": -1105341319 - }, - "name": "I_WY 233_SAT-047D5D92_RC_KEMWYO233B", - "laneWidth": 32700, - "directionality": { - "both": "" - }, - "description": { - "path": { - "offset": { - "ll": { - "nodes": { - "NodeLL": [ - { - "delta": { - "node-LL3": { - "lon": -8338, - "lat": 10897 - } - } - }, - { - "delta": { - "node-LL5": { - "lon": 1507, - "lat": 169431 - } - } - }, - { - "delta": { - "node-LL5": { - "lon": -260004, - "lat": 152391 - } - } - }, - { - "delta": { - "node-LL5": { - "lon": -30606, - "lat": 230849 - } - } - }, - { - "delta": { - "node-LL4": { - "lon": -116741, - "lat": 76938 - } - } - }, - { - "delta": { - "node-LL5": { - "lon": -237069, - "lat": 63754 - } - } - }, - { - "delta": { - "node-LL4": { - "lon": -110373, - "lat": 80674 - } - } - }, - { - "delta": { - "node-LL5": { - "lon": -139739, - "lat": 122423 - } - } - }, - { - "delta": { - "node-LL5": { - "lon": -14891, - "lat": 173179 - } - } - }, - { - "delta": { - "node-LL4": { - "lon": -15201, - "lat": 51728 - } - } - }, - { - "delta": { - "node-LL5": { - "lon": -207792, - "lat": 174741 - } - } - }, - { - "delta": { - "node-LL5": { - "lon": -81993, - "lat": 147705 - } - } - }, - { - "delta": { - "node-LL4": { - "lon": 26456, - "lat": 68121 - } - } - }, - { - "delta": { - "node-LL4": { - "lon": 58918, - "lat": 74751 - } - } - }, - { - "delta": { - "node-LL4": { - "lon": -56248, - "lat": 74095 - } - } - }, - { - "delta": { - "node-LL5": { - "lon": -10762, - "lat": 220792 - } - } - }, - { - "delta": { - "node-LL4": { - "lon": -5356, - "lat": 70780 - } - } - }, - { - "delta": { - "node-LL4": { - "lon": 41398, - "lat": 125487 - } - } - }, - { - "delta": { - "node-LL4": { - "lon": 36074, - "lat": 63967 - } - } - }, - { - "delta": { - "node-LL5": { - "lon": -12787, - "lat": 188434 - } - } - } - ] - } - } - }, - "scale": 0 - } - }, - "id": { - "id": 0, - "region": 0 - }, - "direction": 1110000000001111 - } - }, - "durationTime": 32000, - "notUsed2": 1, - "notUsed3": 1, - "startYear": 2020, - "msgId": { - "roadSignID": { - "viewAngle": 1111111111111111, - "mutcdCode": { - "warning": "" - }, - "position": { - "lat": 418082518, - "long": -1105341319 - } - } - }, - "priority": 5, - "content": { - "advisory": { - "SEQUENCE": [ - { - "item": { - "itis": 5906 - } - }, - { - "item": { - "itis": 4868 - } - } - ] - } - }, - "url": null, - "notUsed": 1, - "notUsed1": 1, - "frameType": { - "advisory": "" - }, - "startTime": 454054 + "sanitized": false, + "recordGeneratedAt": "2020-11-11T07:34:05.151191Z", + "maxDurationTime": 32000, + "odeTimStartDateTime": "2020-11-11T07:36:53.035Z", + "odeReceivedAt": "2020-11-11T07:34:05.220Z" + }, + "payload": { + "data": { + "timeStamp": 454054, + "packetID": "1D6FCDA23022F235E6", + "urlB": "null", + "dataFrames": [ + { + "regions": { + "GeographicalPath": { + "closedPath": "false", + "anchor": { + "lat": 418082518, + "long": -1105341319 + }, + "name": "I_WY 233_SAT-047D5D92_RC_KEMWYO233B", + "laneWidth": 32700, + "directionality": "both", + "description": { + "path": { + "offset": { + "ll": { + "nodes": [ + { + "delta": { + "node-LL3": { + "lon": -8338, + "lat": 10897 + } + } + }, + { + "delta": { + "node-LL5": { + "lon": 1507, + "lat": 169431 + } + } + }, + { + "delta": { + "node-LL5": { + "lon": -260004, + "lat": 152391 + } + } + }, + { + "delta": { + "node-LL5": { + "lon": -30606, + "lat": 230849 + } + } + }, + { + "delta": { + "node-LL4": { + "lon": -116741, + "lat": 76938 + } + } + }, + { + "delta": { + "node-LL5": { + "lon": -237069, + "lat": 63754 + } + } + }, + { + "delta": { + "node-LL4": { + "lon": -110373, + "lat": 80674 + } + } + }, + { + "delta": { + "node-LL5": { + "lon": -139739, + "lat": 122423 + } + } + }, + { + "delta": { + "node-LL5": { + "lon": -14891, + "lat": 173179 + } + } + }, + { + "delta": { + "node-LL4": { + "lon": -15201, + "lat": 51728 + } + } + }, + { + "delta": { + "node-LL5": { + "lon": -207792, + "lat": 174741 + } } - }, - "msgCnt": 16 + }, + { + "delta": { + "node-LL5": { + "lon": -81993, + "lat": 147705 + } + } + }, + { + "delta": { + "node-LL4": { + "lon": 26456, + "lat": 68121 + } + } + }, + { + "delta": { + "node-LL4": { + "lon": 58918, + "lat": 74751 + } + } + }, + { + "delta": { + "node-LL4": { + "lon": -56248, + "lat": 74095 + } + } + }, + { + "delta": { + "node-LL5": { + "lon": -10762, + "lat": 220792 + } + } + }, + { + "delta": { + "node-LL4": { + "lon": -5356, + "lat": 70780 + } + } + }, + { + "delta": { + "node-LL4": { + "lon": 41398, + "lat": 125487 + } + } + }, + { + "delta": { + "node-LL4": { + "lon": 36074, + "lat": 63967 + } + } + }, + { + "delta": { + "node-LL5": { + "lon": -12787, + "lat": 188434 + } + } + } + ] } + }, + "scale": 0 } + }, + "id": { + "id": 0, + "region": 0 + }, + "direction": { + "from000-0to022-5degrees": true, + "from022-5to045-0degrees": true, + "from045-0to067-5degrees": true, + "from067-5to090-0degrees": false, + "from090-0to112-5degrees": false, + "from112-5to135-0degrees": false, + "from135-0to157-5degrees": false, + "from157-5to180-0degrees": false, + "from180-0to202-5degrees": false, + "from202-5to225-0degrees": false, + "from225-0to247-5degrees": false, + "from247-5to270-0degrees": false, + "from270-0to292-5degrees": true, + "from292-5to315-0degrees": true, + "from315-0to337-5degrees": true, + "from337-5to360-0degrees": true + } } - }, - "dataType": "TravelerInformation" + }, + "durationTime": 32000, + "doNotUse3": 0, + "doNotUse4": 0, + "startYear": 2020, + "msgId": { + "roadSignID": { + "viewAngle": { + "from000-0to022-5degrees": true, + "from022-5to045-0degrees": true, + "from045-0to067-5degrees": true, + "from067-5to090-0degrees": true, + "from090-0to112-5degrees": true, + "from112-5to135-0degrees": true, + "from135-0to157-5degrees": true, + "from157-5to180-0degrees": true, + "from180-0to202-5degrees": true, + "from202-5to225-0degrees": true, + "from225-0to247-5degrees": true, + "from247-5to270-0degrees": true, + "from270-0to292-5degrees": true, + "from292-5to315-0degrees": true, + "from315-0to337-5degrees": true, + "from337-5to360-0degrees": true + }, + "mutcdCode": "warning", + "position": { + "lat": 418082518, + "long": -1105341319 + } + } + }, + "priority": 5, + "content": { + "advisory": [ + { + "item": { + "itis": 5906 + } + }, + { + "item": { + "itis": 4868 + } + } + ] + }, + "url": null, + "doNotUse1": 0, + "doNotUse2": 0, + "frameType": "advisory", + "startTime": 454054 + } + ], + "msgCnt": 16 } -} \ No newline at end of file + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.travelerinformation.TravelerInformation" +} diff --git a/logger-kafka-consumer/src/test/resources/TIM_odeTim_Rsus.json b/logger-kafka-consumer/src/test/resources/TIM_odeTim_Rsus.json index ef2374a1b..1c0e0c33b 100644 --- a/logger-kafka-consumer/src/test/resources/TIM_odeTim_Rsus.json +++ b/logger-kafka-consumer/src/test/resources/TIM_odeTim_Rsus.json @@ -1,244 +1,259 @@ { - "metadata": { - "maxDurationTime": 32000, - "odePacketID": "257559968DC2308890", - "odeReceivedAt": "2020-12-14T23:36:41.490Z", - "odeTimStartDateTime": "2020-12-14T23:36:40.718Z", - "payloadType": "us.dot.its.jpo.ode.model.OdeTimPayload", - "recordGeneratedAt": "2020-12-14T23:36:40.965947Z", - "recordGeneratedBy": "TMC", - "request": { - "ode": { - "verb": "PUT", - "version": 3 - }, - "rsus": { - "rsus": { - "rsuIndex": 53, - "rsuRetries": 3, - "rsuTarget": "10.145.12.27", - "rsuTimeout": 5000 - } - }, - "snmp": { - "channel": 178, - "deliverystart": "2020-12-14T23:36:40.718Z", - "deliverystop": "2021-01-06T04:56Z", - "enable": 1, - "interval": 2, - "mode": 1, - "msgid": 31, - "rsuid": 83, - "status": 4 - } - }, - "sanitized": false, - "schemaVersion": 6, - "serialId": { - "bundleId": 2965, - "bundleSize": 1, - "recordId": 0, - "serialNumber": 2965, - "streamId": "78804150-9fa0-4f73-b490-54c507ebc52b" + "metadata": { + "maxDurationTime": 32000, + "odePacketID": "257559968DC2308890", + "odeReceivedAt": "2020-12-14T23:36:41.490Z", + "odeTimStartDateTime": "2020-12-14T23:36:40.718Z", + "payloadType": "us.dot.its.jpo.ode.model.OdeTimPayload", + "recordGeneratedAt": "2020-12-14T23:36:40.965947Z", + "recordGeneratedBy": "TMC", + "request": { + "ode": { + "verb": "PUT", + "version": 3 + }, + "rsus": { + "rsus": { + "rsuIndex": 53, + "rsuRetries": 3, + "rsuTarget": "10.145.12.27", + "rsuTimeout": 5000 } + }, + "snmp": { + "channel": 178, + "deliverystart": "2020-12-14T23:36:40.718Z", + "deliverystop": "2021-01-06T04:56Z", + "enable": 1, + "interval": 2, + "mode": 1, + "msgid": 31, + "rsuid": 83, + "status": 4 + } }, - "payload": { - "data": { - "MessageFrame": { - "messageId": 31, - "value": { - "TravelerInformation": { - "dataFrames": { - "TravelerDataFrame": { - "content": { - "advisory": { - "SEQUENCE": { - "item": { - "itis": 4871 - } - } - } - }, - "durationTime": 32000, - "frameType": { - "advisory": "" - }, - "msgId": { - "roadSignID": { - "mutcdCode": { - "warning": "" - }, - "position": { - "lat": 412696444, - "long": -1054796709 - }, - "viewAngle": 1111111111111111 - } - }, - "priority": 5, - "regions": { - "GeographicalPath": { - "anchor": { - "lat": 412696444, - "long": -1054796709 - }, - "closedPath": { - "false": "" - }, - "description": { - "path": { - "offset": { - "ll": { - "nodes": { - "NodeLL": [ - { - "delta": { - "node-LL3": { - "lat": -10830, - "lon": 12710 - } - } - }, - { - "delta": { - "node-LL3": { - "lat": -20785, - "lon": 26578 - } - } - }, - { - "delta": { - "node-LL3": { - "lat": -6698, - "lon": 16897 - } - } - }, - { - "delta": { - "node-LL4": { - "lat": -5448, - "lon": 56865 - } - } - }, - { - "delta": { - "node-LL3": { - "lat": -14806, - "lon": 32584 - } - } - }, - { - "delta": { - "node-LL3": { - "lat": -15537, - "lon": 32086 - } - } - }, - { - "delta": { - "node-LL4": { - "lat": -18878, - "lon": 51470 - } - } - }, - { - "delta": { - "node-LL4": { - "lat": -55342, - "lon": 88538 - } - } - }, - { - "delta": { - "node-LL3": { - "lat": -23750, - "lon": 21609 - } - } - }, - { - "delta": { - "node-LL4": { - "lat": -117938, - "lon": 73050 - } - } - }, - { - "delta": { - "node-LL4": { - "lat": -56861, - "lon": 14308 - } - } - }, - { - "delta": { - "node-LL3": { - "lat": -28664, - "lon": -2927 - } - } - }, - { - "delta": { - "node-LL4": { - "lat": -41129, - "lon": -17876 - } - } - }, - { - "delta": { - "node-LL3": { - "lat": -14462, - "lon": 667 - } - } - } - ] - } - } - }, - "scale": 0 - } - }, - "direction": "0000111110000000", - "directionality": { - "both": "" - }, - "id": { - "id": 0, - "region": 0 - }, - "laneWidth": 5000, - "name": "I_I 80_RSU-10.145.12.27_RC_test-t-12" - } - }, - "notUsed1": 1, - "notUsed2": 1, - "notUsed3": 1, - "notUsed": 1, - "startTime": 502536, - "startYear": 2020, - "url": null + "sanitized": false, + "schemaVersion": 6, + "serialId": { + "bundleId": 2965, + "bundleSize": 1, + "recordId": 0, + "serialNumber": 2965, + "streamId": "78804150-9fa0-4f73-b490-54c507ebc52b" + } + }, + "payload": { + "data": { + "msgCnt": 2, + "packetID": "257559968DC2308890", + "timeStamp": 502536, + "urlB": "null", + "dataFrames": [ + { + "content": { + "advisory": [ + { + "item": {"itis": 4871} + } + ] + }, + "durationTime": 32000, + "frameType": "advisory", + "msgId": { + "roadSignID": { + "mutcdCode": "warning", + "position": { + "lat": 412696444, + "long": -1054796709 + }, + "viewAngle": { + "from000-0to022-5degrees": true, + "from022-5to045-0degrees": true, + "from045-0to067-5degrees": true, + "from067-5to090-0degrees": true, + "from090-0to112-5degrees": true, + "from112-5to135-0degrees": true, + "from135-0to157-5degrees": true, + "from157-5to180-0degrees": true, + "from180-0to202-5degrees": true, + "from202-5to225-0degrees": true, + "from225-0to247-5degrees": true, + "from247-5to270-0degrees": true, + "from270-0to292-5degrees": true, + "from292-5to315-0degrees": true, + "from315-0to337-5degrees": true, + "from337-5to360-0degrees": true + } + } + }, + "priority": 5, + "regions": [ + { + "anchor": { + "lat": 412696444, + "long": -1054796709 + }, + "closedPath": "false", + "description": { + "path": { + "offset": { + "ll": { + "nodes": [ + { + "delta": { + "node-LL3": { + "lat": -10830, + "lon": 12710 + } + } + }, + { + "delta": { + "node-LL3": { + "lat": -20785, + "lon": 26578 + } + } + }, + { + "delta": { + "node-LL3": { + "lat": -6698, + "lon": 16897 + } + } + }, + { + "delta": { + "node-LL4": { + "lat": -5448, + "lon": 56865 + } + } + }, + { + "delta": { + "node-LL3": { + "lat": -14806, + "lon": 32584 + } + } + }, + { + "delta": { + "node-LL3": { + "lat": -15537, + "lon": 32086 + } + } + }, + { + "delta": { + "node-LL4": { + "lat": -18878, + "lon": 51470 + } + } + }, + { + "delta": { + "node-LL4": { + "lat": -55342, + "lon": 88538 + } + } + }, + { + "delta": { + "node-LL3": { + "lat": -23750, + "lon": 21609 + } + } + }, + { + "delta": { + "node-LL4": { + "lat": -117938, + "lon": 73050 } + } }, - "msgCnt": 2, - "packetID": "257559968DC2308890", - "timeStamp": 502536, - "urlB": null + { + "delta": { + "node-LL4": { + "lat": -56861, + "lon": 14308 + } + } + }, + { + "delta": { + "node-LL3": { + "lat": -28664, + "lon": -2927 + } + } + }, + { + "delta": { + "node-LL4": { + "lat": -41129, + "lon": -17876 + } + } + }, + { + "delta": { + "node-LL3": { + "lat": -14462, + "lon": 667 + } + } + } + ] } + }, + "scale": 0 } + }, + "direction": { + "from000-0to022-5degrees": false, + "from022-5to045-0degrees": false, + "from045-0to067-5degrees": false, + "from067-5to090-0degrees": false, + "from090-0to112-5degrees": true, + "from112-5to135-0degrees": true, + "from135-0to157-5degrees": true, + "from157-5to180-0degrees": true, + "from180-0to202-5degrees": false, + "from202-5to225-0degrees": false, + "from225-0to247-5degrees": false, + "from247-5to270-0degrees": false, + "from270-0to292-5degrees": false, + "from292-5to315-0degrees": false, + "from315-0to337-5degrees": false, + "from337-5to360-0degrees": false + }, + "directionality": "both", + "id": { + "id": 0, + "region": 0 + }, + "laneWidth": 5000, + "name": "I_I 80_RSU-10.145.12.27_RC_test-t-12" } - }, - "dataType": "TravelerInformation" + ], + "doNotUse2": 0, + "doNotUse3": 0, + "doNotUse4": 0, + "doNotUse1": 0, + "startTime": 502536, + "startYear": 2020, + "url": "null" + } + ] } -} \ No newline at end of file + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.travelerinformation.TravelerInformation" +} diff --git a/logger-kafka-consumer/src/test/resources/TIM_unsigned.json b/logger-kafka-consumer/src/test/resources/TIM_unsigned.json deleted file mode 100644 index 3b3d056d3..000000000 --- a/logger-kafka-consumer/src/test/resources/TIM_unsigned.json +++ /dev/null @@ -1,213 +0,0 @@ -{ - "metadata": { - "request": { - "ode": { - "verb": "POST", - "version": "3" - }, - "sdw": { - "recordId": "60F53BDF", - "serviceRegion": { - "nwCorner": { - "latitude": "44.09328877", - "longitude": "-107.96034055" - }, - "seCorner": { - "latitude": "44.07225981", - "longitude": "-107.95401624" - } - }, - "ttl": "oneyear" - } - }, - "recordGeneratedBy": "TMC", - "schemaVersion": "6", - "payloadType": "us.dot.its.jpo.ode.model.OdeTimPayload", - "odePacketID": "20F6CCABC034423CB3", - "serialId": { - "recordId": "0", - "serialNumber": "76194", - "streamId": "662dd7d9-72d8-4b8a-a25a-4efa45b48936", - "bundleSize": "1", - "bundleId": "76194" - }, - "sanitized": "false", - "recordGeneratedAt": "2022-11-18T17:12:40.652239Z", - "maxDurationTime": "32000", - "odeTimStartDateTime": "2022-09-12T00:00:00.000Z", - "odeReceivedAt": "2022-11-18T17:12:40.682413Z" - }, - "payload": { - "data": { - "AdvisorySituationData": { - "recordID": "60F53BDF", - "timeToLive": "5", - "serviceRegion": { - "nwCorner": { - "lat": "440932888", - "long": "-1079603406" - }, - "seCorner": { - "lat": "440722598", - "long": "-1079540162" - } - }, - "asdmDetails": { - "advisoryMessage": { - "Ieee1609Dot2Data": { - "protocolVersion": "3", - "content": { - "unsecuredData": { - "MessageFrame": { - "messageId": "31", - "value": { - "TravelerInformation": { - "timeStamp": "463272", - "packetID": "20F6CCABC034423CB3", - "urlB": "null", - "dataFrames": { - "TravelerDataFrame": { - "regions": { - "GeographicalPath": { - "closedPath": { - "false": "" - }, - "anchor": { - "lat": "440947086", - "long": "-1079550305" - }, - "name": "D_WY 433_SAT-60F53BDF_RW_39683-0", - "laneWidth": "5000", - "directionality": { - "both": "" - }, - "description": { - "path": { - "offset": { - "ll": { - "nodes": { - "NodeLL": [ - { - "delta": { - "node-LL3": { - "lon": "3768", - "lat": "-14198" - } - } - }, - { - "delta": { - "node-LL3": { - "lon": "6374", - "lat": "-28466" - } - } - }, - { - "delta": { - "node-LL3": { - "lon": "-5520", - "lat": "-28521" - } - } - }, - { - "delta": { - "node-LL4": { - "lon": "-52848", - "lat": "-124572" - } - } - }, - { - "delta": { - "node-LL3": { - "lon": "-4875", - "lat": "-28730" - } - } - } - ] - } - } - }, - "scale": "0" - } - }, - "id": { - "id": "0", - "region": "0" - }, - "direction": "0000000110000000" - } - }, - "durationTime": "32000", - "notUsed2": "1", - "notUsed3": "1", - "startYear": "2022", - "msgId": { - "roadSignID": { - "viewAngle": "1111111111111111", - "mutcdCode": { - "warning": "" - }, - "position": { - "lat": "440947086", - "long": "-1079550305" - } - } - }, - "priority": "5", - "content": { - "workZone": { - "SEQUENCE": { - "item": { - "itis": "1025" - } - } - } - }, - "url": "null", - "notUsed": "1", - "notUsed1": "1", - "frameType": { - "advisory": "" - }, - "startTime": "365760" - } - }, - "msgCnt": "1" - } - } - } - } - } - } - }, - "startTime": { - "month": "0", - "hour": "31", - "year": "0", - "day": "0", - "minute": "60" - }, - "stopTime": { - "month": "0", - "hour": "31", - "year": "0", - "day": "0", - "minute": "60" - }, - "distType": "02", - "asdmType": "2", - "asdmID": "427DA360" - }, - "requestID": "427DA360", - "groupID": "00000000", - "dialogID": "156", - "seqID": "5" - } - }, - "dataType": "TravelerInformation" - } -} \ No newline at end of file diff --git a/logger-kafka-consumer/src/test/resources/rxMsg_TIM_OdeOutput.json b/logger-kafka-consumer/src/test/resources/rxMsg_TIM_OdeOutput.json index f61c08d40..169a60727 100644 --- a/logger-kafka-consumer/src/test/resources/rxMsg_TIM_OdeOutput.json +++ b/logger-kafka-consumer/src/test/resources/rxMsg_TIM_OdeOutput.json @@ -82,10 +82,10 @@ } }, "durationTime": 32000, - "notUsed2": 0, - "notUsed3": 0, - "notUsed": 0, - "notUsed1": 0, + "doNotUse3": 0, + "doNotUse4": 0, + "doNotUse1": 0, + "doNotUse2": 0, "frameType": { "roadSignage": "" }, diff --git a/logger-kafka-consumer/src/test/resources/rxMsg_TIM_OdeOutput_NullMetadata.json b/logger-kafka-consumer/src/test/resources/rxMsg_TIM_OdeOutput_NullMetadata.json index d756c5efd..99c9e6fae 100644 --- a/logger-kafka-consumer/src/test/resources/rxMsg_TIM_OdeOutput_NullMetadata.json +++ b/logger-kafka-consumer/src/test/resources/rxMsg_TIM_OdeOutput_NullMetadata.json @@ -54,10 +54,10 @@ } }, "durationTime": 32000, - "notUsed2": 0, - "notUsed3": 0, - "notUsed": 0, - "notUsed1": 0, + "doNotUse3": 0, + "doNotUse4": 0, + "doNotUse1": 0, + "doNotUse2": 0, "frameType": { "roadSignage": "" }, diff --git a/milepost-graph-db/Dockerfile b/milepost-graph-db/Dockerfile deleted file mode 100644 index 535a78921..000000000 --- a/milepost-graph-db/Dockerfile +++ /dev/null @@ -1,53 +0,0 @@ -FROM openjdk:8-jre-slim - -ENV NEO4J_SHA256=fb435b11494cde475f748f057a192bcbd8580c7445b380afe9ff52311f334bfe \ - NEO4J_TARBALL=neo4j-community-3.5.14-unix.tar.gz \ - NEO4J_EDITION=community \ - NEO4J_HOME="/var/lib/neo4j" \ - TINI_VERSION="v0.18.0" \ - TINI_SHA256="12d20136605531b09a2c2dac02ccee85e1b874eb322ef6baf7561cd93f93c855" -ARG NEO4J_URI=https://dist.neo4j.org/neo4j-community-3.5.14-unix.tar.gz - -RUN addgroup --system neo4j && adduser --system --no-create-home --home "${NEO4J_HOME}" --ingroup neo4j neo4j - -COPY ./local-package/* /tmp/ - -RUN apt update \ - && apt install -y curl wget gosu jq \ - && curl -L --fail --silent --show-error "https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini" > /sbin/tini \ - && echo "${TINI_SHA256} /sbin/tini" | sha256sum -c --strict --quiet \ - && chmod +x /sbin/tini \ - && curl --fail --silent --show-error --location --remote-name ${NEO4J_URI} \ - && echo "${NEO4J_SHA256} ${NEO4J_TARBALL}" | sha256sum -c --strict --quiet \ - && tar --extract --file ${NEO4J_TARBALL} --directory /var/lib \ - && mv /var/lib/neo4j-* "${NEO4J_HOME}" \ - && rm ${NEO4J_TARBALL} \ - && mv "${NEO4J_HOME}"/data /data \ - && mv "${NEO4J_HOME}"/logs /logs \ - && chown -R neo4j:neo4j /data \ - && chmod -R 777 /data \ - && chown -R neo4j:neo4j /logs \ - && chmod -R 777 /logs \ - && chown -R neo4j:neo4j "${NEO4J_HOME}" \ - && chmod -R 777 "${NEO4J_HOME}" \ - && ln -s /data "${NEO4J_HOME}"/data \ - && ln -s /logs "${NEO4J_HOME}"/logs \ - && mv /tmp/neo4jlabs-plugins.json /neo4jlabs-plugins.json \ - && rm -rf /tmp/* \ - && rm -rf /var/lib/apt/lists/* \ - && apt-get -y purge --auto-remove curl - -ENV PATH "${NEO4J_HOME}"/bin:$PATH - -WORKDIR "${NEO4J_HOME}" - -VOLUME /data /logs - -COPY docker-entrypoint.sh /docker-entrypoint.sh - -RUN chmod -R 777 /docker-entrypoint.sh - -EXPOSE 7474 7473 7687 - -ENTRYPOINT ["/sbin/tini", "-g", "--", "/docker-entrypoint.sh"] -CMD ["neo4j"] \ No newline at end of file diff --git a/milepost-graph-db/README.md b/milepost-graph-db/README.md deleted file mode 100644 index b39ae52f6..000000000 --- a/milepost-graph-db/README.md +++ /dev/null @@ -1,80 +0,0 @@ -# Milepost Neo4j Database - ---- - -

Dockerized Neo4j database used to efficiently query paths across Wyoming highways -
-

- -## 📝 Table of Contents - -- [About](#about) -- [Getting Started](#getting_started) -- [Deployment](#deployment) -- [Usage](#usage) -- [Built Using](#built_using) -- [TODO](#TODO) - -## 🧐 About - -This is a simple stand-up of a dockerized Neo4j database used to query paths instead of the milepost_vw. It includes several scripts to run to get a version up and running with the Milepost layer. - -## 🏁 Getting Started - -These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See [deployment](#deployment) for notes on how to deploy the project on a live system. - -### Prerequisites - -The only real prerequisite here is to have Docker up and running. The scripts take care of the rest. - -### Installing - -A docker-compose file has been provided here for simplicity to enable running this container individually. To run the container simply execute the following command: - -``` -docker-compose up --build -d -``` - -This will install Neo4j in a docker container without authentication and expose the following ports for access: - -``` -6474: http -6473: https -6687: bolt -``` - -To view the database in browser navigate to http://localhost:6474/browser. - -## 🎈 Usage - -The initial check-in includes the base set of data to avoid having to run the scripts. If you need to update the data in the Neo4j database the following steps may be taken. - -1. Export milepost_vw_new data as .csv from the database (include headers) - ``` - SELECT - * - FROM - milepost_vw_new - ORDER BY - common_name, - milepost - ``` -2. Copy the new export.csv file to the specified [import folder](./neo-data/import) -3. Run [import_commands.cmd](./import_commands.cmd) to import new data. Note that you will need to remove any existing data you are replacing through the browser UI - -## 🚀 Deployment - -Deployment should be very similar to running this locally since everything will be handled in docker. This application can be added to the main docker-compose file in the wyocv applications folder for simplicity. If a different location is desired to store data, specify those locations in the docker-compose.yml file and copy the following folders to the appropriate locations: - -- [conf](./neo-data/conf) - holds custom configuration -- [data/databases](./neo-data/data/databases) - houses the data -- [import](./neo-data/import) - used to import any data -- [plugins](./neo-data/plugins) - holds the APOC and Graph Algorithm jar files used to access data - -## ⛏️ Built Using - -- [Neo4j](https://neo4j.com/) - Database - -## TODO - -The [generate_relationships.sh](./generate_relationships.sh) and [generate_named_relationships.cql](generate_named_relationships.cql) are to be used in conjunction to generate a more efficient and communicative set of import statements to be ran rather than the existing [import.cql](./neo-data/import/import.cql). The existing import scripts run for a substantial time (~12+ hours on dev machine) and do not communicate progress very well. diff --git a/milepost-graph-db/docker-compose.yml b/milepost-graph-db/docker-compose.yml deleted file mode 100644 index 57e10305e..000000000 --- a/milepost-graph-db/docker-compose.yml +++ /dev/null @@ -1,24 +0,0 @@ -version: '3.7' -services: - milepost-graph: - build: . - # image: neo4j:3.5.14 - ports: - - '6474:7474' - - '6473:7473' - - '6687:7687' - volumes: - - type: bind - source: ./neo-data/plugins - target: /var/lib/neo4j/plugins - - type: bind - source: ./neo-data/import - target: /var/lib/neo4j/import - - type: bind - source: ./neo-data/conf - target: /var/lib/neo4j/conf - - type: bind - source: ./neo-data/data - target: /var/lib/neo4j/data - environment: - NEO4J_AUTH: none diff --git a/milepost-graph-db/docker-entrypoint.sh b/milepost-graph-db/docker-entrypoint.sh deleted file mode 100644 index 424b86be6..000000000 --- a/milepost-graph-db/docker-entrypoint.sh +++ /dev/null @@ -1,446 +0,0 @@ -#!/bin/bash -eu - -cmd="$1" - -function running_as_root -{ - test "$(id -u)" = "0" -} - -function secure_mode_enabled -{ - test "${SECURE_FILE_PERMISSIONS:=no}" = "yes" -} - -function containsElement -{ - local e match="$1" - shift - for e; do [[ "$e" == "$match" ]] && return 0; done - return 1 -} - -function is_readable -{ - # this code is fairly ugly but works no matter who this script is running as. - # It would be nice if the writability tests could use this logic somehow. - local _file=${1} - perm=$(stat -c %a "${_file}") - - # everyone permission - if [[ ${perm:2:1} -ge 4 ]]; then - return 0 - fi - # owner permissions - if [[ ${perm:0:1} -ge 4 ]]; then - if [[ "$(stat -c %U ${_file})" = "${userid}" ]] || [[ "$(stat -c %u ${_file})" = "${userid}" ]]; then - return 0 - fi - fi - # group permissions - if [[ ${perm:1:1} -ge 4 ]]; then - if containsElement "$(stat -c %g ${_file})" "${groups[@]}" || containsElement "$(stat -c %G ${_file})" "${groups[@]}" ; then - return 0 - fi - fi - return 1 -} - -function is_writable -{ - # It would be nice if this and the is_readable function could combine somehow - local _file=${1} - perm=$(stat -c %a "${_file}") - - # everyone permission - if containsElement ${perm:2:1} 2 3 6 7; then - return 0 - fi - # owner permissions - if containsElement ${perm:0:1} 2 3 6 7; then - if [[ "$(stat -c %U ${_file})" = "${userid}" ]] || [[ "$(stat -c %u ${_file})" = "${userid}" ]]; then - return 0 - fi - fi - # group permissions - if containsElement ${perm:1:1} 2 3 6 7; then - if containsElement "$(stat -c %g ${_file})" "${groups[@]}" || containsElement "$(stat -c %G ${_file})" "${groups[@]}" ; then - return 0 - fi - fi - return 1 -} - - -function print_permissions_advice_and_fail -{ - _directory=${1} - echo >&2 " -Folder ${_directory} is not accessible for user: ${userid} or group ${groupid} or groups ${groups[@]}, this is commonly a file permissions issue on the mounted folder. - -Hints to solve the issue: -1) Make sure the folder exists before mounting it. Docker will create the folder using root permissions before starting the Neo4j container. The root permissions disallow Neo4j from writing to the mounted folder. -2) Pass the folder owner's user ID and group ID to docker run, so that docker runs as that user. -If the folder is owned by the current user, this can be done by adding this flag to your docker run command: - --user=\$(id -u):\$(id -g) - " - exit 1 -} - -function check_mounted_folder_readable -{ - local _directory=${1} - if ! is_readable "${_directory}"; then - print_permissions_advice_and_fail "${_directory}" - fi -} - -function check_mounted_folder_with_chown -{ -# The /data and /log directory are a bit different because they are very likely to be mounted by the user but not -# necessarily writable. -# This depends on whether a user ID is passed to the container and which folders are mounted. -# -# No user ID passed to container: -# 1) No folders are mounted. -# The /data and /log folder are owned by neo4j by default, so should be writable already. -# 2) Both /log and /data are mounted. -# This means on start up, /data and /logs are owned by an unknown user and we should chown them to neo4j for -# backwards compatibility. -# -# User ID passed to container: -# 1) Both /data and /logs are mounted -# The /data and /logs folders are owned by an unknown user but we *should* have rw permission to them. -# That should be verified and error (helpfully) if not. -# 2) User mounts /data or /logs *but not both* -# The unmounted folder is still owned by neo4j, which should already be writable. The mounted folder should -# have rw permissions through user id. This should be verified. -# 3) No folders are mounted. -# The /data and /log folder are owned by neo4j by default, and these are already writable by the user. -# (This is a very unlikely use case). - - local mountFolder=${1} - if running_as_root; then - if ! is_writable "${mountFolder}" && ! secure_mode_enabled; then - # warn that we're about to chown the folder and then chown it - echo "Warning: Folder mounted to \"${mountFolder}\" is not writable from inside container. Changing folder owner to ${userid}." - chown -R "${userid}":"${groupid}" "${mountFolder}" - fi - else - if [[ ! -w "${mountFolder}" ]] && [[ "$(stat -c %U ${mountFolder})" != "neo4j" ]]; then - print_permissions_advice_and_fail "${mountFolder}" - fi - fi -} - -function load_plugin_from_github -{ - # Load a plugin at runtime. The provided github repository must have a versions.json on the master branch with the - # correct format. - local _plugin_name="${1}" #e.g. apoc, graph-algorithms, graph-ql - - local _plugins_dir="${NEO4J_HOME}/plugins" - if [ -d /plugins ]; then - local _plugins_dir="/plugins" - fi - local _versions_json_url="$(jq --raw-output "with_entries( select(.key==\"${_plugin_name}\") ) | to_entries[] | .value" /neo4jlabs-plugins.json )" - # Using the same name for the plugin irrespective of version ensures we don't end up with different versions of the same plugin - local _destination="${_plugins_dir}/${_plugin_name}.jar" - local _neo4j_version="$(neo4j --version | cut -d' ' -f2)" - - # Now we call out to github to get the versions.json for this plugin and we parse that to find the url for the correct plugin jar for our neo4j version - echo "Fetching versions.json for Plugin '${_plugin_name}' from ${_versions_json_url}" - local _versions_json="$(wget -q --timeout 300 --tries 30 -O - "${_versions_json_url}")" - local _plugin_jar_url="$(echo "${_versions_json}" | jq --raw-output ".[] | select(.neo4j==\"${_neo4j_version}\") | .jar")" - if [[ -z "${_plugin_jar_url}" ]]; then - echo >&2 "No jar URL found for version '${_neo4j_version}' in versions.json from '${_versions_json_url}'" - echo >&2 "${_versions_json}" - fi - echo "Installing Plugin '${_plugin_name}' from ${_plugin_jar_url} to ${_destination} " - wget -q --timeout 300 --tries 30 --output-document="${_destination}" "${_plugin_jar_url}" - - if ! is_readable "${_destination}"; then - echo >&2 "Plugin at '${_destination}' is not readable" - exit 1 - fi -} - -# If we're running as root, then run as the neo4j user. Otherwise -# docker is running with --user and we simply use that user. Note -# that su-exec, despite its name, does not replicate the functionality -# of exec, so we need to use both -if running_as_root; then - userid="neo4j" - groupid="neo4j" - groups=($(id -G neo4j)) - exec_cmd="exec gosu neo4j:neo4j" -else - userid="$(id -u)" - groupid="$(id -g)" - groups=($(id -G)) - exec_cmd="exec" -fi -readonly userid -readonly groupid -readonly groups -readonly exec_cmd - - -# Need to chown the home directory - but a user might have mounted a -# volume here (notably a conf volume). So take care not to chown -# volumes (stuff not owned by neo4j) -if running_as_root; then - # Non-recursive chown for the base directory - chown "${userid}":"${groupid}" "${NEO4J_HOME}" - chmod 700 "${NEO4J_HOME}" - find "${NEO4J_HOME}" -mindepth 1 -maxdepth 1 -user root -type d -exec chown -R ${userid}:${groupid} {} \; - find "${NEO4J_HOME}" -mindepth 1 -maxdepth 1 -user root -type d -exec chmod -R 700 {} \; -fi - -# Only prompt for license agreement if command contains "neo4j" in it -if [[ "${cmd}" == *"neo4j"* ]]; then - if [ "${NEO4J_EDITION}" == "enterprise" ]; then - if [ "${NEO4J_ACCEPT_LICENSE_AGREEMENT:=no}" != "yes" ]; then - echo >&2 " -In order to use Neo4j Enterprise Edition you must accept the license agreement. - -(c) Neo4j Sweden AB. 2019. All Rights Reserved. -Use of this Software without a proper commercial license with Neo4j, -Inc. or its affiliates is prohibited. - -Email inquiries can be directed to: licensing@neo4j.com - -More information is also available at: https://neo4j.com/licensing/ - - -To accept the license agreement set the environment variable -NEO4J_ACCEPT_LICENSE_AGREEMENT=yes - -To do this you can use the following docker argument: - - --env=NEO4J_ACCEPT_LICENSE_AGREEMENT=yes -" - exit 1 - fi - fi -fi - -# Env variable naming convention: -# - prefix NEO4J_ -# - double underscore char '__' instead of single underscore '_' char in the setting name -# - underscore char '_' instead of dot '.' char in the setting name -# Example: -# NEO4J_dbms_tx__log_rotation_retention__policy env variable to set -# dbms.tx_log.rotation.retention_policy setting - -# Backward compatibility - map old hardcoded env variables into new naming convention (if they aren't set already) -# Set some to default values if unset -: ${NEO4J_dbms_tx__log_rotation_retention__policy:=${NEO4J_dbms_txLog_rotation_retentionPolicy:-"100M size"}} -: ${NEO4J_wrapper_java_additional:=${NEO4J_UDC_SOURCE:-"-Dneo4j.ext.udc.source=docker"}} -: ${NEO4J_dbms_unmanaged__extension__classes:=${NEO4J_dbms_unmanagedExtensionClasses:-}} -: ${NEO4J_dbms_allow__format__migration:=${NEO4J_dbms_allowFormatMigration:-}} -: ${NEO4J_dbms_connectors_default__advertised__address:=${NEO4J_dbms_connectors_defaultAdvertisedAddress:-}} - -if [ "${NEO4J_EDITION}" == "enterprise" ]; - then - : ${NEO4J_causal__clustering_expected__core__cluster__size:=${NEO4J_causalClustering_expectedCoreClusterSize:-}} - : ${NEO4J_causal__clustering_initial__discovery__members:=${NEO4J_causalClustering_initialDiscoveryMembers:-}} - : ${NEO4J_causal__clustering_discovery__advertised__address:=${NEO4J_causalClustering_discoveryAdvertisedAddress:-"$(hostname):5000"}} - : ${NEO4J_causal__clustering_transaction__advertised__address:=${NEO4J_causalClustering_transactionAdvertisedAddress:-"$(hostname):6000"}} - : ${NEO4J_causal__clustering_raft__advertised__address:=${NEO4J_causalClustering_raftAdvertisedAddress:-"$(hostname):7000"}} - # Custom settings for dockerized neo4j - : ${NEO4J_causal__clustering_discovery__advertised__address:=$(hostname):5000} - : ${NEO4J_causal__clustering_transaction__advertised__address:=$(hostname):6000} - : ${NEO4J_causal__clustering_raft__advertised__address:=$(hostname):7000} -fi - -# unset old hardcoded unsupported env variables -unset NEO4J_dbms_txLog_rotation_retentionPolicy NEO4J_UDC_SOURCE \ - NEO4J_dbms_unmanagedExtensionClasses NEO4J_dbms_allowFormatMigration \ - NEO4J_dbms_connectors_defaultAdvertisedAddress NEO4J_ha_serverId \ - NEO4J_ha_initialHosts NEO4J_causalClustering_expectedCoreClusterSize \ - NEO4J_causalClustering_initialDiscoveryMembers \ - NEO4J_causalClustering_discoveryListenAddress \ - NEO4J_causalClustering_discoveryAdvertisedAddress \ - NEO4J_causalClustering_transactionListenAddress \ - NEO4J_causalClustering_transactionAdvertisedAddress \ - NEO4J_causalClustering_raftListenAddress \ - NEO4J_causalClustering_raftAdvertisedAddress - -if [ -d /conf ]; then - if secure_mode_enabled; then - check_mounted_folder_readable "/conf" - fi - find /conf -type f -exec cp {} "${NEO4J_HOME}"/conf \; -fi - -if [ -d /ssl ]; then - if secure_mode_enabled; then - check_mounted_folder_readable "/ssl" - fi - : ${NEO4J_dbms_directories_certificates:="/ssl"} -fi - -if [ -d /plugins ]; then - if secure_mode_enabled; then - if [[ ! -z "${NEO4JLABS_PLUGINS:-}" ]]; then - # We need write permissions - check_mounted_folder_with_chown "/plugins" - fi - check_mounted_folder_readable "/plugins" - fi - : ${NEO4J_dbms_directories_plugins:="/plugins"} -fi - -if [ -d /import ]; then - if secure_mode_enabled; then - check_mounted_folder_readable "/import" - fi - : ${NEO4J_dbms_directories_import:="/import"} -fi - -if [ -d /metrics ]; then - if secure_mode_enabled; then - check_mounted_folder_readable "/metrics" - fi - : ${NEO4J_dbms_directories_metrics:="/metrics"} -fi - -if [ -d /logs ]; then - check_mounted_folder_with_chown "/logs" - : ${NEO4J_dbms_directories_logs:="/logs"} -fi - -if [ -d /data ]; then - check_mounted_folder_with_chown "/data" - if [ -d /data/databases ]; then - check_mounted_folder_with_chown "/data/databases" - fi - if [ -d /data/dbms ]; then - check_mounted_folder_with_chown "/data/dbms" - fi -fi - - -# set the neo4j initial password only if you run the database server -if [ "${cmd}" == "neo4j" ]; then - if [ "${NEO4J_AUTH:-}" == "none" ]; then - NEO4J_dbms_security_auth__enabled=false - elif [[ "${NEO4J_AUTH:-}" == neo4j/* ]]; then - password="${NEO4J_AUTH#neo4j/}" - if [ "${password}" == "neo4j" ]; then - echo >&2 "Invalid value for password. It cannot be 'neo4j', which is the default." - exit 1 - fi - - if running_as_root; then - # running set-initial-password as root will create subfolders to /data as root, causing startup fail when neo4j can't read or write the /data/dbms folder - # creating the folder first will avoid that - mkdir -p /data/dbms - chown "${userid}":"${groupid}" /data/dbms - fi - # Will exit with error if users already exist (and print a message explaining that) - # we probably don't want the message though, since it throws an error message on restarting the container. - neo4j-admin set-initial-password "${password}" 2>/dev/null || true - elif [ -n "${NEO4J_AUTH:-}" ]; then - echo >&2 "Invalid value for NEO4J_AUTH: '${NEO4J_AUTH}'" - exit 1 - fi -fi - -declare -A COMMUNITY -declare -A ENTERPRISE - -COMMUNITY=( - [dbms.tx_log.rotation.retention_policy]="100M size" - [dbms.memory.pagecache.size]="2G" - [dbms.memory.heap.initial_size]="2G" - [dbms.memory.heap.max_size]="2G" - [dbms.connectors.default_listen_address]="0.0.0.0" - [dbms.connector.https.listen_address]="0.0.0.0:7473" - [dbms.connector.http.listen_address]="0.0.0.0:7474" - [dbms.connector.bolt.listen_address]="0.0.0.0:7687" -) - -ENTERPRISE=( - [causal_clustering.transaction_listen_address]="0.0.0.0:6000" - [causal_clustering.raft_listen_address]="0.0.0.0:7000" - [causal_clustering.discovery_listen_address]="0.0.0.0:5000" -) - -for conf in ${!COMMUNITY[@]} ; do - - if ! grep -q "^$conf" "${NEO4J_HOME}"/conf/neo4j.conf - then - echo -e "\n"$conf=${COMMUNITY[$conf]} >> "${NEO4J_HOME}"/conf/neo4j.conf - fi -done - -for conf in ${!ENTERPRISE[@]} ; do - - if [ "${NEO4J_EDITION}" == "enterprise" ]; - then - if ! grep -q "^$conf" "${NEO4J_HOME}"/conf/neo4j.conf - then - echo -e "\n"$conf=${ENTERPRISE[$conf]} >> "${NEO4J_HOME}"/conf/neo4j.conf - fi - fi -done - -#The udc.source=tarball should be replaced by udc.source=docker in both dbms.jvm.additional and wrapper.java.additional -#Using sed to replace only this part will allow the custom configs to be added after, separated by a ,. -if grep -q "udc.source=tarball" "${NEO4J_HOME}"/conf/neo4j.conf; then - sed -i -e 's/udc.source=tarball/udc.source=docker/g' "${NEO4J_HOME}"/conf/neo4j.conf -fi -#The udc.source should always be set to docker by default and we have to allow also custom configs to be added after that. -#In this case, this piece of code helps to add the default value and a , to support custom configs after. -if ! grep -q "dbms.jvm.additional=-Dunsupported.dbms.udc.source=docker" "${NEO4J_HOME}"/conf/neo4j.conf; then - sed -i -e 's/dbms.jvm.additional=/dbms.jvm.additional=-Dunsupported.dbms.udc.source=docker,/g' "${NEO4J_HOME}"/conf/neo4j.conf -fi - -# list env variables with prefix NEO4J_ and create settings from them -unset NEO4J_AUTH NEO4J_SHA256 NEO4J_TARBALL -for i in $( set | grep ^NEO4J_ | awk -F'=' '{print $1}' | sort -rn ); do - setting=$(echo ${i} | sed 's|^NEO4J_||' | sed 's|_|.|g' | sed 's|\.\.|_|g') - value=$(echo ${!i}) - # Don't allow settings with no value or settings that start with a number (neo4j converts settings to env variables and you cannot have an env variable that starts with a number) - if [[ -n ${value} ]]; then - if [[ ! "${setting}" =~ ^[0-9]+.*$ ]]; then - if grep -q -F "${setting}=" "${NEO4J_HOME}"/conf/neo4j.conf; then - # Remove any lines containing the setting already - sed --in-place "/^${setting}=.*/d" "${NEO4J_HOME}"/conf/neo4j.conf - fi - # Then always append setting to file - echo "${setting}=${value}" >> "${NEO4J_HOME}"/conf/neo4j.conf - else - echo >&2 "WARNING: ${setting} not written to conf file because settings that start with a number are not permitted" - fi - fi -done - -if [[ ! -z "${NEO4JLABS_PLUGINS:-}" ]]; then - # NEO4JLABS_PLUGINS should be a json array of plugins like '["graph-algorithms", "apoc-procedures", "streams", "graphql"]' - for plugin_name in $(echo "${NEO4JLABS_PLUGINS}" | jq --raw-output '.[]'); do - load_plugin_from_github "${plugin_name}" - done -fi - -[ -f "${EXTENSION_SCRIPT:-}" ] && . ${EXTENSION_SCRIPT} - -if [ "${cmd}" == "dump-config" ]; then - if ! is_writable "/conf"; then - print_permissions_advice_and_fail "/conf" - fi - cp --recursive "${NEO4J_HOME}"/conf/* /conf - echo "Config Dumped" - exit 0 -fi - -# Use su-exec to drop privileges to neo4j user -# Note that su-exec, despite its name, does not replicate the -# functionality of exec, so we need to use both -if [ "${cmd}" == "neo4j" ]; then - ${exec_cmd} neo4j console -else - ${exec_cmd} "$@" -fi \ No newline at end of file diff --git a/milepost-graph-db/generate_named_relationships.cql b/milepost-graph-db/generate_named_relationships.cql deleted file mode 100644 index b4e3be6f2..000000000 --- a/milepost-graph-db/generate_named_relationships.cql +++ /dev/null @@ -1,22 +0,0 @@ -//relationship match, updated -match (n:Milepost{CommonName: "COMMON_NAME"}) -where n.Direction in ["I", "B"] -match (m:Milepost) -where m.CommonName = n.CommonName and m.Milepost > n.Milepost and m.Direction in ["I", "B"] and distance(point({longitude:n.Longitude, latitude:n.Latitude}), point({longitude:m.Longitude, latitude:m.Latitude})) < 804.672 -with n, min(m.Milepost) as minM -match (nxt:Milepost) -where nxt.CommonName = n.CommonName and nxt.Milepost = minM and nxt.Direction in ["I", "B"] and distance(point({longitude:n.Longitude, latitude:n.Latitude}), point({longitude:nxt.Longitude, latitude:nxt.Latitude})) < 804.672 -call apoc.create.relationship(n, replace(n.CommonName, " ", "_")+"_I", {Direction:n.Direction, CommonName:n.CommonName},nxt) yield rel -return rel; - - -//relationship match, updated -match (n:Milepost{CommonName: "COMMON_NAME"}) -where n.Direction in ["D", "B"] -match (m:Milepost) -where m.CommonName = n.CommonName and m.Milepost < n.Milepost and m.Direction in ["D", "B"] and distance(point({longitude:n.Longitude, latitude:n.Latitude}), point({longitude:m.Longitude, latitude:m.Latitude})) < 804.672 -with n, max(m.Milepost) as maxM -match (nxt:Milepost) -where nxt.CommonName = n.CommonName and nxt.Milepost = maxM and nxt.Direction in ["D", "B"] and distance(point({longitude:n.Longitude, latitude:n.Latitude}), point({longitude:nxt.Longitude, latitude:nxt.Latitude})) < 804.672 -call apoc.create.relationship(n, replace(n.CommonName, " ", "_")+"_D", {Direction:n.Direction, CommonName:n.CommonName},nxt) yield rel -return rel; \ No newline at end of file diff --git a/milepost-graph-db/generate_relationships.sh b/milepost-graph-db/generate_relationships.sh deleted file mode 100644 index 6a335d1e2..000000000 --- a/milepost-graph-db/generate_relationships.sh +++ /dev/null @@ -1,4 +0,0 @@ -cat common_names.txt | -while read line; do -sed "s/COMMON_NAME/$line/g" generate_named_relationships.cql >> neo-data/import/tmp.cql -done \ No newline at end of file diff --git a/milepost-graph-db/import_commands.cmd b/milepost-graph-db/import_commands.cmd deleted file mode 100644 index d7bedb3bf..000000000 --- a/milepost-graph-db/import_commands.cmd +++ /dev/null @@ -1 +0,0 @@ -docker exec -it milepost-graph-db_milepost-graph_1 bash -c "cat import/import.cql | bin/cypher-shell" \ No newline at end of file diff --git a/milepost-graph-db/local-package/.sentinel b/milepost-graph-db/local-package/.sentinel deleted file mode 100644 index e69de29bb..000000000 diff --git a/milepost-graph-db/local-package/neo4jlabs-plugins.json b/milepost-graph-db/local-package/neo4jlabs-plugins.json deleted file mode 100644 index fa77de243..000000000 --- a/milepost-graph-db/local-package/neo4jlabs-plugins.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "apoc": "https://neo4j-contrib.github.io/neo4j-apoc-procedures/versions.json", - "streams": "https://neo4j-contrib.github.io/neo4j-streams/versions.json", - "graphql": "https://neo4j-graphql.github.io/neo4j-graphql/versions.json", - "graph-algorithms": "https://neo4j-contrib.github.io/neo4j-graph-algorithms/versions.json", - "_testing": "http://host.testcontainers.internal:3000/versions.json" -} diff --git a/milepost-graph-db/neo-data/conf/neo4j.conf b/milepost-graph-db/neo-data/conf/neo4j.conf deleted file mode 100644 index 5a9eb4bc4..000000000 --- a/milepost-graph-db/neo-data/conf/neo4j.conf +++ /dev/null @@ -1,20 +0,0 @@ - -dbms.connector.https.listen_address=0.0.0.0:7473 - -dbms.connectors.default_listen_address=0.0.0.0 - -dbms.connector.http.listen_address=0.0.0.0:7474 - -dbms.connector.bolt.listen_address=0.0.0.0:7687 - -dbms.memory.pagecache.size=2G -dbms.memory.heap.initial_size=2G -dbms.memory.heap.max_size=2G - -dbms.security.procedures.unrestricted=algo.*,apoc.* -wrapper.java.additional=-Dneo4j.ext.udc.source=docker -dbms.tx_log.rotation.retention_policy=100M size -dbms.security.auth_enabled=false -dbms.directories.logs=/logs -HOME=/var/lib/neo4j -EDITION=community diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore b/milepost-graph-db/neo-data/data/databases/graph.db/neostore deleted file mode 100644 index 945fd2cc6..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.counts.db.a b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.counts.db.a deleted file mode 100644 index 42f972e98..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.counts.db.a and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.id b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.id deleted file mode 100644 index 22fd28d4e..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.id and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.labelscanstore.db b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.labelscanstore.db deleted file mode 100644 index 010bd0127..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.labelscanstore.db and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.labeltokenstore.db b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.labeltokenstore.db deleted file mode 100644 index e69de29bb..000000000 diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.labeltokenstore.db.id b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.labeltokenstore.db.id deleted file mode 100644 index 22fd28d4e..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.labeltokenstore.db.id and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.labeltokenstore.db.names b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.labeltokenstore.db.names deleted file mode 100644 index ce6529796..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.labeltokenstore.db.names and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.labeltokenstore.db.names.id b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.labeltokenstore.db.names.id deleted file mode 100644 index 60d6e05be..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.labeltokenstore.db.names.id and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.nodestore.db b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.nodestore.db deleted file mode 100644 index e69de29bb..000000000 diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.nodestore.db.id b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.nodestore.db.id deleted file mode 100644 index 22fd28d4e..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.nodestore.db.id and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.nodestore.db.labels b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.nodestore.db.labels deleted file mode 100644 index df8ce2a58..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.nodestore.db.labels and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.nodestore.db.labels.id b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.nodestore.db.labels.id deleted file mode 100644 index 60d6e05be..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.nodestore.db.labels.id and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db deleted file mode 100644 index e69de29bb..000000000 diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.arrays b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.arrays deleted file mode 100644 index d19598d74..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.arrays and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.arrays.id b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.arrays.id deleted file mode 100644 index 60d6e05be..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.arrays.id and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.id b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.id deleted file mode 100644 index 22fd28d4e..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.id and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.index b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.index deleted file mode 100644 index e69de29bb..000000000 diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.index.id b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.index.id deleted file mode 100644 index 22fd28d4e..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.index.id and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.index.keys b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.index.keys deleted file mode 100644 index ce6529796..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.index.keys and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.index.keys.id b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.index.keys.id deleted file mode 100644 index 60d6e05be..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.index.keys.id and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.strings b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.strings deleted file mode 100644 index d19598d74..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.strings and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.strings.id b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.strings.id deleted file mode 100644 index 60d6e05be..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.propertystore.db.strings.id and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshipgroupstore.db b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshipgroupstore.db deleted file mode 100644 index b72840ef5..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshipgroupstore.db and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshipgroupstore.db.id b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshipgroupstore.db.id deleted file mode 100644 index 60d6e05be..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshipgroupstore.db.id and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshipstore.db b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshipstore.db deleted file mode 100644 index e69de29bb..000000000 diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshipstore.db.id b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshipstore.db.id deleted file mode 100644 index 22fd28d4e..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshipstore.db.id and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshiptypestore.db b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshiptypestore.db deleted file mode 100644 index e69de29bb..000000000 diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshiptypestore.db.id b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshiptypestore.db.id deleted file mode 100644 index 22fd28d4e..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshiptypestore.db.id and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshiptypestore.db.names b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshiptypestore.db.names deleted file mode 100644 index ce6529796..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshiptypestore.db.names and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshiptypestore.db.names.id b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshiptypestore.db.names.id deleted file mode 100644 index 60d6e05be..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.relationshiptypestore.db.names.id and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.schemastore.db b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.schemastore.db deleted file mode 100644 index df8ce2a58..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.schemastore.db and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.schemastore.db.id b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.schemastore.db.id deleted file mode 100644 index 60d6e05be..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.schemastore.db.id and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.transaction.db.0 b/milepost-graph-db/neo-data/data/databases/graph.db/neostore.transaction.db.0 deleted file mode 100644 index b1aade4ed..000000000 Binary files a/milepost-graph-db/neo-data/data/databases/graph.db/neostore.transaction.db.0 and /dev/null differ diff --git a/milepost-graph-db/neo-data/data/databases/store_lock b/milepost-graph-db/neo-data/data/databases/store_lock deleted file mode 100644 index e69de29bb..000000000 diff --git a/milepost-graph-db/neo-data/import/export.csv b/milepost-graph-db/neo-data/import/export.csv deleted file mode 100644 index 534884de7..000000000 --- a/milepost-graph-db/neo-data/import/export.csv +++ /dev/null @@ -1,3 +0,0 @@ -"COMMON_NAME","DIRECTION","MILEPOST","LATITUDE","LONGITUDE" -"Example Route","D",0,40.99821606,-104.90694278 -"Example Route","I",0,40.9982158,-104.90666619 \ No newline at end of file diff --git a/milepost-graph-db/neo-data/import/import.cql b/milepost-graph-db/neo-data/import/import.cql deleted file mode 100644 index 3a24451ab..000000000 --- a/milepost-graph-db/neo-data/import/import.cql +++ /dev/null @@ -1,18 +0,0 @@ -load csv with headers from "file:///export.csv" as row -merge (m:Milepost {CommonName:row.COMMON_NAME, Milepost:toFloat(row.MILEPOST), Direction: row.DIRECTION, Latitude:toFloat(row.LATITUDE), Longitude:toFloat(row.LONGITUDE)}); - -//////////////////////////////////////////////////// -match (n:Milepost) -where n.Direction in ["I", "B"] -call apoc.cypher.run('with {n} as n match (m:Milepost) where m.CommonName = n.CommonName and n.Milepost < m.Milepost and m.Direction in ["I","B"] with m, distance(point({longitude:n.Longitude, latitude:n.Latitude}), point({longitude:m.Longitude, latitude:m.Latitude})) as dst where dst < 8046.72 return m order by m.Milepost,dst limit 1', {n:n}) yield value as val -with n, val.m as nxt -call apoc.create.relationship(n, replace(n.CommonName, " ", "_")+"_I", {Direction:n.Direction, CommonName:n.CommonName},nxt) yield rel -return rel; - - -match (n:Milepost) -where n.Direction in ["D", "B"] -call apoc.cypher.run('with {n} as n match (m:Milepost) where m.CommonName = n.CommonName and n.Milepost > m.Milepost and m.Direction in ["D","B"] with m, distance(point({longitude:n.Longitude, latitude:n.Latitude}), point({longitude:m.Longitude, latitude:m.Latitude})) as dst where dst < 8046.72 return m order by m.Milepost desc, dst limit 1', {n:n}) yield value as val -with n, val.m as nxt -call apoc.create.relationship(n, replace(n.CommonName, " ", "_")+"_D", {Direction:n.Direction, CommonName:n.CommonName},nxt) yield rel -return rel; \ No newline at end of file diff --git a/milepost-graph-db/neo-data/plugins/apoc-3.5.0.9.jar b/milepost-graph-db/neo-data/plugins/apoc-3.5.0.9.jar deleted file mode 100644 index 9020b284d..000000000 Binary files a/milepost-graph-db/neo-data/plugins/apoc-3.5.0.9.jar and /dev/null differ diff --git a/milepost-graph-db/neo-data/plugins/graphAlgorithms-3.5.14.0.jar b/milepost-graph-db/neo-data/plugins/graphAlgorithms-3.5.14.0.jar deleted file mode 100644 index 934558518..000000000 Binary files a/milepost-graph-db/neo-data/plugins/graphAlgorithms-3.5.14.0.jar and /dev/null differ diff --git a/move-jars.sh b/move-jars.sh new file mode 100755 index 000000000..76a1c40e7 --- /dev/null +++ b/move-jars.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# This script moves all .jar files in the target directory of each subdirectory to the subdirectory itself +# This is necessary because the Dockerfiles expect the .jar files to be in the same directory as the Dockerfile + +# for each directory, move (directory)/target/*.jar to (directory)/ +for d in */; do + # not all directories have a target directory, so we need to check for its existence + if [ -d "$d/target" ]; then + mv "$d/target"/*.jar "$d" + fi +done \ No newline at end of file diff --git a/ode-data-logger/Dockerfile b/ode-data-logger/Dockerfile index 3f8bcaab5..d3c7eb8ff 100644 --- a/ode-data-logger/Dockerfile +++ b/ode-data-logger/Dockerfile @@ -1,5 +1,5 @@ FROM maven:3.8-eclipse-temurin-21-alpine -ADD . /home/wyocv/wyocv_applications/ode-data-logger +ADD . /home/timm/timm_applications/ode-data-logger -CMD java -jar /home/wyocv/wyocv_applications/ode-data-logger/ode-data-logger-1.4.0-SNAPSHOT.jar \ No newline at end of file +CMD java -jar /home/timm/timm_applications/ode-data-logger/ode-data-logger-2.0.0.jar \ No newline at end of file diff --git a/ode-data-logger/README.md b/ode-data-logger/README.md index edf91b2b8..429e97d96 100644 --- a/ode-data-logger/README.md +++ b/ode-data-logger/README.md @@ -25,7 +25,7 @@ These instructions will get you a copy of the project up and running on your loc - Maven is provided by the dev container's base image. ### Docker -The following instructions are intended to be executed from the root directory of the WyoCV project: +The following instructions are intended to be executed from the root directory of the TIMM project: 1. Reopen the project in the provided dev container by clicking on the blue button in the bottom left corner of the window and selecting "Reopen in Container". If Docker isn't running, start it and try again. 1. Open a terminal in the dev container by clicking on the `Terminal` menu and selecting `New Terminal` 1. Compile the project by running the following command: @@ -89,7 +89,7 @@ To run the application using the provided launch configuration, follow these ste 1. Click the green play button to start the application ## Deployment -This application is deployed using Docker, and is part of the larger WyoCVApplication suite. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. +This application is deployed using Docker, and is part of the larger TIM Manager. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. ## Configuration **SOME OF THESE PROPERTIES ARE SENSITIVE. DO NOT PUBLISH THEM TO VERSION CONTROL** diff --git a/ode-data-logger/pom.xml b/ode-data-logger/pom.xml index 9c6c0e665..6520bd579 100644 --- a/ode-data-logger/pom.xml +++ b/ode-data-logger/pom.xml @@ -3,9 +3,9 @@ 4.0.0 - com.wyocv - wyo-cv - 1.4.0-SNAPSHOT + com.timm + tim-manager + 2.0.0 ode-data-logger diff --git a/ode-mongo-logger/Dockerfile b/ode-mongo-logger/Dockerfile index d50d5c06f..7784762e8 100644 --- a/ode-mongo-logger/Dockerfile +++ b/ode-mongo-logger/Dockerfile @@ -1,5 +1,5 @@ FROM maven:3.8-eclipse-temurin-21-alpine -ADD . /home/wyocv/wyocv_applications/ode-mongo-logger +ADD . /home/timm/timm_applications/ode-mongo-logger -CMD java -jar /home/wyocv/wyocv_applications/ode-mongo-logger/ode-mongo-logger-1.4.0-SNAPSHOT.jar +CMD java -jar /home/timm/timm_applications/ode-mongo-logger/ode-mongo-logger-2.0.0.jar diff --git a/ode-mongo-logger/README.md b/ode-mongo-logger/README.md index 13d23f0f0..7aaa30798 100644 --- a/ode-mongo-logger/README.md +++ b/ode-mongo-logger/README.md @@ -1,7 +1,7 @@ # WYDOT ODE Data Logger ![ODE Mongo Logger Architecture Diagram](./docs/diagrams/ode-mongo-logger-architecture.drawio.png) -The `ODE Mongo Logger` module listens for messages on a specified Kafka topic and writes them to a MongoDB database. The module is designed to be deployed as a Docker container and is part of the WyoCV Suite of applications. +The `ODE Mongo Logger` module listens for messages on a specified Kafka topic and writes them to a MongoDB database. The module is designed to be deployed as a Docker container and is part of the TIM Manager of applications. ## Table of Contents - [Installation](#installation) @@ -24,7 +24,7 @@ These instructions will get you a copy of the project up and running on your loc - Maven is provided by the dev container's base image. ### Docker -The following instructions are intended to be executed from the root directory of the WyoCV project: +The following instructions are intended to be executed from the root directory of the TIMM project: 1. Reopen the project in the provided dev container by clicking on the blue button in the bottom left corner of the window and selecting "Reopen in Container". If Docker isn't running, start it and try again. 1. Open a terminal in the dev container by clicking on the `Terminal` menu and selecting `New Terminal` 1. Compile the project by running the following command: @@ -87,7 +87,7 @@ To run the application using the provided launch configuration, follow these ste 1. Click the green play button to start the application ## Deployment -This application is deployed using Docker, and is part of the larger WyoCVApplication suite. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. +This application is deployed using Docker, and is part of the larger TIM Manager. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. ## Configuration **SOME OF THESE PROPERTIES ARE SENSITIVE. DO NOT PUBLISH THEM TO VERSION CONTROL** diff --git a/ode-mongo-logger/pom.xml b/ode-mongo-logger/pom.xml index fd11a35fb..a4770435f 100644 --- a/ode-mongo-logger/pom.xml +++ b/ode-mongo-logger/pom.xml @@ -2,9 +2,9 @@ 4.0.0 - com.wyocv - wyo-cv - 1.4.0-SNAPSHOT + com.timm + tim-manager + 2.0.0 diff --git a/ode-mongo-logger/src/main/java/com/trihydro/mongologger/app/loggers/MongoLogger.java b/ode-mongo-logger/src/main/java/com/trihydro/mongologger/app/loggers/MongoLogger.java index be1d249fc..e54cf41bc 100644 --- a/ode-mongo-logger/src/main/java/com/trihydro/mongologger/app/loggers/MongoLogger.java +++ b/ode-mongo-logger/src/main/java/com/trihydro/mongologger/app/loggers/MongoLogger.java @@ -1,7 +1,6 @@ package com.trihydro.mongologger.app.loggers; import java.util.ArrayList; -import java.util.Arrays; import com.mongodb.MongoClientSettings; import com.mongodb.MongoCredential; @@ -14,34 +13,35 @@ import com.trihydro.library.helpers.Utility; import com.trihydro.mongologger.app.MongoLoggerConfiguration; +import java.util.List; import org.bson.Document; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class MongoLogger { - - private String serverAddress; - private String username; - private String password; - private String databaseName; - private String authDatabaseName; - private MongoCredential credential; - private Utility utility; - private EmailHelper emailHelper; - private MongoLoggerConfiguration config; + private final Utility utility; + private final EmailHelper emailHelper; + private final String databaseName; + private final String[] alertAddresses; + private final MongoClient mongoClient; @Autowired - public void InjectDependencies(MongoLoggerConfiguration _config, Utility _utility, EmailHelper _emailHelper) { - config = _config; - username = config.getMongoUsername(); // the user name - databaseName = config.getMongoDatabase(); // the name of the database to deposit records into - authDatabaseName = config.getMongoAuthDatabase(); // the name of the database in which the user is defined - password = config.getMongoPassword(); // the password as a character array - serverAddress = config.getMongoHost(); - credential = MongoCredential.createCredential(username, authDatabaseName, password.toCharArray()); - utility = _utility; - emailHelper = _emailHelper; + public MongoLogger(MongoLoggerConfiguration config, Utility utility, EmailHelper emailHelper) { + this.emailHelper = emailHelper; + this.utility = utility; + this.mongoClient = configureMongoClient(config); + this.databaseName = config.getMongoDatabase(); // the name of the database to deposit records into + this.alertAddresses = config.getAlertAddresses(); // the email addresses to send alerts to + } + + private MongoClient configureMongoClient(MongoLoggerConfiguration config) { + MongoCredential credential = MongoCredential.createCredential(config.getMongoUsername(), config.getMongoAuthDatabase(), config.getMongoPassword().toCharArray()); + var hosts = List.of(new ServerAddress(config.getMongoHost(), 27017)); + var settings = MongoClientSettings.builder() + .applyToClusterSettings(builder -> builder.hosts(hosts)).credential(credential) + .build(); + return MongoClients.create(settings); } public void logTim(String[] timRecord) { @@ -57,20 +57,14 @@ public void logDriverAlert(String[] driverAlertRecord) { } public void logMultipleToCollection(String[] records, String collectionName) { - ArrayList docs = new ArrayList(); + ArrayList docs = new ArrayList<>(); for (String rec : records) { docs.add(Document.parse(rec)); } - if (docs.size() > 0) { - MongoClient mongoClient = null; + if (!docs.isEmpty()) { try { - mongoClient = MongoClients.create(MongoClientSettings.builder() - .applyToClusterSettings( - builder -> builder.hosts(Arrays.asList(new ServerAddress(serverAddress, 27017)))) - .credential(credential).build()); - MongoDatabase database = mongoClient.getDatabase(databaseName); MongoCollection collection = database.getCollection(collectionName); collection.insertMany(docs); @@ -83,14 +77,11 @@ public void logMultipleToCollection(String[] records, String collectionName) { body += "Exception:
"; body += ex.getMessage(); try { - emailHelper.SendEmail(config.getAlertAddresses(), "MongoLogger Failed to Connect to MongoDB", body); + emailHelper.SendEmail(alertAddresses, "MongoLogger Failed to Connect to MongoDB", body); } catch (Exception e) { + utility.logWithDate("Error sending email: " + e.getMessage()); e.printStackTrace(); } - } finally { - if (mongoClient != null) { - mongoClient.close(); - } } } } diff --git a/ode-wrapper/Dockerfile b/ode-wrapper/Dockerfile index 43ad44fad..e2beb8b2b 100644 --- a/ode-wrapper/Dockerfile +++ b/ode-wrapper/Dockerfile @@ -1,5 +1,5 @@ FROM maven:3.8-eclipse-temurin-21-alpine -ADD . /home/wyocv/wyocv_applications/ode-wrapper +ADD . /home/timm/timm_applications/ode-wrapper -CMD java -jar /home/wyocv/wyocv_applications/ode-wrapper/ode-wrapper-1.4.0-SNAPSHOT.jar +CMD java -jar /home/timm/timm_applications/ode-wrapper/ode-wrapper-2.0.0.jar diff --git a/ode-wrapper/README.md b/ode-wrapper/README.md index 91011f05d..43cd69116 100644 --- a/ode-wrapper/README.md +++ b/ode-wrapper/README.md @@ -17,7 +17,7 @@ These instructions will get you a copy of the project up and running on your loc | Dependency | Direct/Indirect | Link | | ---------- | --------------- | ---- | | [CV Data Controller](../cv-data-controller/README.md) | Direct | [README](../cv-data-controller/README.md) | -| [WyoCV Database](../db-scripts/pgsql) | Direct | [PGSQL Scripts](../db-scripts/pgsql) | +| [TIMM Database](../db-scripts/pgsql) | Direct | [PGSQL Scripts](../db-scripts/pgsql) | | [ODE](https://www.github.com/usdot-jpo-ode/jpo-ode) | Indirect | [GitHub](https://www.github.com/usdot-jpo-ode/jpo-ode) | ### Compile-Time Dependencies @@ -26,7 +26,7 @@ These instructions will get you a copy of the project up and running on your loc - ODE JARs are installed using [post-create.sh](../.devcontainer/post-create.sh). ### Docker -The following instructions are intended to be executed from the root directory of the WyoCV project: +The following instructions are intended to be executed from the root directory of the TIMM project: 1. Reopen the project in the provided dev container by clicking on the blue button in the bottom left corner of the window and selecting "Reopen in Container". If Docker isn't running, start it and try again. 1. Open a terminal in the dev container by clicking on the `Terminal` menu and selecting `New Terminal` 1. Compile the project by running the following command: @@ -101,7 +101,7 @@ To run the application and the cv-data-controller using the provided compound la 1. Click the green play button to start the application ## Deployment -This application is deployed using Docker, and is part of the larger WyoCVApplication suite. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. +This application is deployed using Docker, and is part of the larger TIM Manager. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. ## Configuration **SOME OF THESE PROPERTIES ARE SENSITIVE. DO NOT PUBLISH THEM TO VERSION CONTROL** diff --git a/ode-wrapper/pom.xml b/ode-wrapper/pom.xml index 0ab5650b3..7acf07b54 100644 --- a/ode-wrapper/pom.xml +++ b/ode-wrapper/pom.xml @@ -7,9 +7,9 @@ - com.wyocv - wyo-cv - 1.4.0-SNAPSHOT + com.timm + tim-manager + 2.0.0 diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/UtilityController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/UtilityController.java index 40e4a1653..26131ecba 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/UtilityController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/UtilityController.java @@ -1,5 +1,6 @@ package com.trihydro.odewrapper.controller; +import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -57,9 +58,9 @@ class RsuClearSuccess { public UtilityController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, OdeService _odeService, RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, - Utility _utility, TimGenerationHelper _timGenerationHelper) { + Utility _utility, TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { super(_basicConfiguration, _wydotTimService, _timTypeService, _setItisCodes, _activeTimService, - _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper); + _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler); this.odeService = _odeService; } diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimBaseController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimBaseController.java index 276123c33..506eb93ce 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimBaseController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimBaseController.java @@ -1,5 +1,7 @@ package com.trihydro.odewrapper.controller; +import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; +import com.trihydro.library.model.TimUpdateModel; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.LocalDate; @@ -14,6 +16,7 @@ import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.Objects; import java.util.TimeZone; import com.google.gson.Gson; @@ -43,32 +46,37 @@ import com.trihydro.odewrapper.model.WydotTimRc; import com.trihydro.odewrapper.model.WydotTimVsl; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import us.dot.its.jpo.ode.plugin.j2735.timstorage.FrameType.TravelerInfoType; @Component +@Slf4j public abstract class WydotTimBaseController { protected static BasicConfiguration configuration; protected WydotTimService wydotTimService; protected TimTypeService timTypeService; private TimType timType = null; - private SetItisCodes setItisCodes; + private final SetItisCodes setItisCodes; protected ActiveTimService activeTimService; protected RestTemplateProvider restTemplateProvider; MilepostReduction milepostReduction; protected Utility utility; protected TimGenerationHelper timGenerationHelper; + private final IdenticalPointsExceptionHandler identicalPointsExceptionHandler; protected static Gson gson = new Gson(); private List timTypes; - public WydotTimBaseController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, - TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, - RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, - TimGenerationHelper _timGenerationHelper) { + public WydotTimBaseController(BasicConfiguration _basicConfiguration, + WydotTimService _wydotTimService, TimTypeService _timTypeService, + SetItisCodes _setItisCodes, ActiveTimService _activeTimService, + RestTemplateProvider _restTemplateProvider, + MilepostReduction _milepostReduction, Utility _utility, + TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { configuration = _basicConfiguration; wydotTimService = _wydotTimService; timTypeService = _timTypeService; @@ -78,6 +86,7 @@ public WydotTimBaseController(BasicConfiguration _basicConfiguration, WydotTimSe milepostReduction = _milepostReduction; utility = _utility; timGenerationHelper = _timGenerationHelper; + this.identicalPointsExceptionHandler = identicalPointsExceptionHandler; } protected String getStartTime() { @@ -107,8 +116,9 @@ protected ControllerResult validateInputParking(WydotTimParking tim) { List resultMessages = new ArrayList(); // get route number - if (tim.getDirection() != null) + if (tim.getDirection() != null) { result.setDirection(tim.getDirection()); + } result.setClientId(tim.getClientId()); @@ -118,8 +128,9 @@ protected ControllerResult validateInputParking(WydotTimParking tim) { result.setRoute(tim.getRoute()); } // if direction is not i/d/b fail - if (!tim.getDirection().equalsIgnoreCase("i") && !tim.getDirection().equalsIgnoreCase("d") - && !tim.getDirection().equalsIgnoreCase("b")) { + if (!tim.getDirection().equalsIgnoreCase("i") && + !tim.getDirection().equalsIgnoreCase("d") && + !tim.getDirection().equalsIgnoreCase("b")) { resultMessages.add("direction not supported"); } if (tim.getMileMarker() != null && tim.getMileMarker() < 0) { @@ -134,8 +145,9 @@ protected ControllerResult validateInputParking(WydotTimParking tim) { // set itis codes List itisCodes = setItisCodes.setItisCodesParking(tim); - if (itisCodes.size() == 0) + if (itisCodes.isEmpty()) { resultMessages.add("No ITIS codes found"); + } result.setItisCodes(itisCodes); tim.setItisCodes(itisCodes); @@ -149,8 +161,9 @@ public ControllerResult validateInputIncident(WydotTimIncident tim) { List resultMessages = new ArrayList(); // get route number - if (tim.getDirection() != null) + if (tim.getDirection() != null) { result.setDirection(tim.getDirection()); + } if (tim.getIncidentId() != null) { result.setClientId(tim.getIncidentId()); tim.setClientId(tim.getIncidentId()); @@ -164,8 +177,9 @@ public ControllerResult validateInputIncident(WydotTimIncident tim) { } // if direction is not i/d/b fail - if (!tim.getDirection().equalsIgnoreCase("i") && !tim.getDirection().equalsIgnoreCase("d") - && !tim.getDirection().equalsIgnoreCase("b")) { + if (!tim.getDirection().equalsIgnoreCase("i") && + !tim.getDirection().equalsIgnoreCase("d") && + !tim.getDirection().equalsIgnoreCase("b")) { resultMessages.add("direction not supported"); } if (tim.getIncidentId() == null) { @@ -181,8 +195,9 @@ public ControllerResult validateInputIncident(WydotTimIncident tim) { // set itis codes List itisCodes = setItisCodes.setItisCodesIncident(tim); - if (itisCodes.size() == 0) + if (itisCodes.isEmpty()) { resultMessages.add("No ITIS codes found"); + } result.setItisCodes(itisCodes); tim.setItisCodes(itisCodes); @@ -196,8 +211,9 @@ protected ControllerResult validateInputRw(WydotTimRw tim) { List resultMessages = new ArrayList(); // get route number - if (tim.getDirection() != null) + if (tim.getDirection() != null) { result.setDirection(tim.getDirection()); + } if (tim.getId() != null) { result.setClientId(tim.getId()); tim.setClientId(tim.getId()); @@ -211,8 +227,9 @@ protected ControllerResult validateInputRw(WydotTimRw tim) { } // if direction is not i/d/b fail - if (!tim.getDirection().equalsIgnoreCase("i") && !tim.getDirection().equalsIgnoreCase("d") - && !tim.getDirection().equalsIgnoreCase("b")) { + if (!tim.getDirection().equalsIgnoreCase("i") && + !tim.getDirection().equalsIgnoreCase("d") && + !tim.getDirection().equalsIgnoreCase("b")) { resultMessages.add("direction not supported"); } if (tim.getStartPoint() == null || !tim.getStartPoint().isValid()) { @@ -234,23 +251,28 @@ protected ControllerResult validateInputRw(WydotTimRw tim) { resultMessages.add("Null value for schedStart"); } else { try { - var convertedDate = LocalDate.parse(tim.getSchedStart(), DateTimeFormatter.ISO_LOCAL_DATE); + var convertedDate = + LocalDate.parse(tim.getSchedStart(), DateTimeFormatter.ISO_LOCAL_DATE); var startOfDay = convertedDate.atStartOfDay(ZoneId.systemDefault()); tim.setSchedStart(getIsoDateTimeString(startOfDay)); } catch (DateTimeParseException e) { - resultMessages.add("Bad value supplied for schedStart. Should follow the format: yyyy-MM-dd"); + resultMessages.add( + "Bad value supplied for schedStart. Should follow the format: yyyy-MM-dd"); e.printStackTrace(); } } if (tim.getSchedEnd() != null) { try { - var convertedDate = LocalDate.parse(tim.getSchedEnd(), DateTimeFormatter.ISO_LOCAL_DATE); + var convertedDate = + LocalDate.parse(tim.getSchedEnd(), DateTimeFormatter.ISO_LOCAL_DATE); // LocalTime.MAX sets the time to 11:59 PM. This is especially important when // construction is only scheduled for a day (ex. 5-12 to 5-12) - var endOfDay = ZonedDateTime.of(LocalDateTime.of(convertedDate, LocalTime.MAX), ZoneId.systemDefault()); + var endOfDay = ZonedDateTime.of(LocalDateTime.of(convertedDate, LocalTime.MAX), + ZoneId.systemDefault()); tim.setSchedEnd(getIsoDateTimeString(endOfDay)); } catch (DateTimeParseException e) { - resultMessages.add("Bad value supplied for schedEnd. Should follow the format: yyyy-MM-dd"); + resultMessages.add( + "Bad value supplied for schedEnd. Should follow the format: yyyy-MM-dd"); e.printStackTrace(); } } @@ -278,8 +300,9 @@ protected ControllerResult validateInputRw(WydotTimRw tim) { // set itis codes List itisCodes = setItisCodes.setItisCodesRw(tim); - if (itisCodes.size() == 0) + if (itisCodes.isEmpty()) { resultMessages.add("No ITIS codes found"); + } result.setItisCodes(itisCodes); tim.setItisCodes(itisCodes); @@ -298,17 +321,18 @@ public boolean routeSupported(String route) { // return routes.contains(route); // Since routes are not loaded, assume all route are supported. - return !route.isEmpty() && route.length() > 0; + return !route.isEmpty(); } protected ControllerResult validateInputRc(WydotTimRc tim) { ControllerResult result = new ControllerResult(); - List resultMessages = new ArrayList(); + List resultMessages = new ArrayList<>(); // get route number - if (tim.getDirection() != null) + if (tim.getDirection() != null) { result.setDirection(tim.getDirection()); + } if (tim.getRoute() == null || !routeSupported(tim.getRoute())) { resultMessages.add("route not supported"); @@ -316,8 +340,9 @@ protected ControllerResult validateInputRc(WydotTimRc tim) { result.setRoute(tim.getRoute()); } // if direction is not i/d/b fail - if (tim.getDirection() != null && !tim.getDirection().equalsIgnoreCase("i") - && !tim.getDirection().equalsIgnoreCase("d") && !tim.getDirection().equalsIgnoreCase("b")) { + if (tim.getDirection() != null && !tim.getDirection().equalsIgnoreCase("i") && + !tim.getDirection().equalsIgnoreCase("d") && + !tim.getDirection().equalsIgnoreCase("b")) { resultMessages.add("direction not supported"); } if (tim.getStartPoint() == null || !tim.getStartPoint().isValid()) { @@ -340,8 +365,9 @@ protected ControllerResult validateInputRc(WydotTimRc tim) { // set itis codes List itisCodes = setItisCodes.setItisCodesRc(tim); - if (itisCodes.size() == 0) + if (itisCodes.isEmpty()) { resultMessages.add("No ITIS codes found"); + } result.setItisCodes(itisCodes); tim.setItisCodes(itisCodes); @@ -351,7 +377,7 @@ protected ControllerResult validateInputRc(WydotTimRc tim) { protected ControllerResult validateRcAc(WydotTimRc allClear) { ControllerResult result = new ControllerResult(); - List resultMessages = new ArrayList(); + List resultMessages = new ArrayList<>(); // All Clear must have a valid CLIENT_ID and DIRECTION if (StringUtils.isBlank(allClear.getRoadCode())) { @@ -363,8 +389,9 @@ protected ControllerResult validateRcAc(WydotTimRc allClear) { if (allClear.getDirection() == null) { resultMessages.add("Null value for direction"); - } else if (!allClear.getDirection().equalsIgnoreCase("i") && !allClear.getDirection().equalsIgnoreCase("d") - && !allClear.getDirection().equalsIgnoreCase("b")) { + } else if (!allClear.getDirection().equalsIgnoreCase("i") && + !allClear.getDirection().equalsIgnoreCase("d") && + !allClear.getDirection().equalsIgnoreCase("b")) { resultMessages.add("direction not supported"); } else { result.setDirection(allClear.getDirection()); @@ -377,11 +404,12 @@ protected ControllerResult validateRcAc(WydotTimRc allClear) { protected ControllerResult validateInputVsl(WydotTimVsl tim) { ControllerResult result = new ControllerResult(); - List resultMessages = new ArrayList(); + List resultMessages = new ArrayList<>(); // get route number - if (tim.getDirection() != null) + if (tim.getDirection() != null) { result.setDirection(tim.getDirection()); + } if (tim.getRoute() == null || !routeSupported(tim.getRoute())) { resultMessages.add("route not supported"); @@ -390,8 +418,9 @@ protected ControllerResult validateInputVsl(WydotTimVsl tim) { } // if direction is not i/d/b fail - if (!tim.getDirection().equalsIgnoreCase("i") && !tim.getDirection().equalsIgnoreCase("d") - && !tim.getDirection().equalsIgnoreCase("b")) { + if (!tim.getDirection().equalsIgnoreCase("i") && + !tim.getDirection().equalsIgnoreCase("d") && + !tim.getDirection().equalsIgnoreCase("b")) { resultMessages.add("direction not supported"); } if (tim.getStartPoint() == null || !tim.getStartPoint().isValid()) { @@ -414,8 +443,9 @@ protected ControllerResult validateInputVsl(WydotTimVsl tim) { // set itis codes List itisCodes = setItisCodes.setItisCodesVsl(tim); - if (itisCodes.size() == 0) + if (itisCodes.isEmpty()) { resultMessages.add("No ITIS codes found"); + } result.setItisCodes(itisCodes); tim.setItisCodes(itisCodes); @@ -426,11 +456,12 @@ protected ControllerResult validateInputVsl(WydotTimVsl tim) { protected ControllerResult validateInputCc(WydotTimRc tim) { ControllerResult result = new ControllerResult(); - List resultMessages = new ArrayList(); + List resultMessages = new ArrayList<>(); // get route number - if (tim.getDirection() != null) + if (tim.getDirection() != null) { result.setDirection(tim.getDirection()); + } if (tim.getSegment() != null) { result.setClientId(tim.getSegment()); @@ -446,8 +477,9 @@ protected ControllerResult validateInputCc(WydotTimRc tim) { } // if direction is not i/d/b fail - if (!tim.getDirection().equalsIgnoreCase("i") && !tim.getDirection().equalsIgnoreCase("d") - && !tim.getDirection().equalsIgnoreCase("b")) { + if (!tim.getDirection().equalsIgnoreCase("i") && + !tim.getDirection().equalsIgnoreCase("d") && + !tim.getDirection().equalsIgnoreCase("b")) { resultMessages.add("direction not supported"); } if (tim.getStartPoint() == null || !tim.getStartPoint().isValid()) { @@ -468,8 +500,9 @@ protected ControllerResult validateInputCc(WydotTimRc tim) { // set itis codes List itisCodes = setItisCodes.setItisCodesFromAdvisoryArray(tim); - if (itisCodes.size() == 0) + if (itisCodes.isEmpty()) { resultMessages.add("No ITIS codes found"); + } result.setItisCodes(itisCodes); tim.setItisCodes(itisCodes); @@ -493,27 +526,35 @@ public boolean isValidAction(String action) { return true; } else if (action.contains("delay_")) { String[] actionSplit = action.split("_"); - if (actionSplit.length < 2) + if (actionSplit.length < 2) { return false; - if (!actionSplit[0].equals("delay")) + } + if (!actionSplit[0].equals("delay")) { return false; - if (!StringUtils.isNumeric(actionSplit[1])) + } + if (!StringUtils.isNumeric(actionSplit[1])) { return false; - if (Float.parseFloat(actionSplit[1]) < 0) + } + if (Float.parseFloat(actionSplit[1]) < 0) { return false; + } return true; } else if (action.equals("prepareStop")) { return true; } else if (action.contains("reduceSpeed_")) { String[] actionSplit = action.split("_"); - if (actionSplit.length < 2) + if (actionSplit.length < 2) { return false; - if (!actionSplit[0].equals("reduceSpeed")) + } + if (!actionSplit[0].equals("reduceSpeed")) { return false; - if (!StringUtils.isNumeric(actionSplit[1])) + } + if (!StringUtils.isNumeric(actionSplit[1])) { return false; - if (Float.parseFloat(actionSplit[1]) < 0) + } + if (Float.parseFloat(actionSplit[1]) < 0) { return false; + } return true; } return false; @@ -521,14 +562,15 @@ public boolean isValidAction(String action) { protected ControllerResult validateInputBowr(WydotTimBowr tim) { ControllerResult toReturn = new ControllerResult(); - List resultMessages = new ArrayList(); + List resultMessages = new ArrayList<>(); // check direction if (tim.getDirection() != null) { toReturn.setDirection(tim.getDirection()); } - if (tim.getDirection() != null && !tim.getDirection().equalsIgnoreCase("i") - && !tim.getDirection().equalsIgnoreCase("d") && !tim.getDirection().equalsIgnoreCase("b")) { + if (tim.getDirection() != null && !tim.getDirection().equalsIgnoreCase("i") && + !tim.getDirection().equalsIgnoreCase("d") && + !tim.getDirection().equalsIgnoreCase("b")) { resultMessages.add("direction not supported"); } @@ -596,8 +638,9 @@ protected ControllerResult validateInputBowr(WydotTimBowr tim) { return toReturn; } - public void processRequest(WydotTim wydotTim, TimType timType, String startDateTime, String endDateTime, Integer pk, - ContentEnum content, TravelerInfoType frameType) { + public void processRequest(WydotTim wydotTim, TimType timType, String startDateTime, + String endDateTime, Integer pk, ContentEnum content, + TravelerInfoType frameType) { if (wydotTim.getDirection().equalsIgnoreCase("b")) { var iTim = wydotTim.copy(); @@ -605,45 +648,51 @@ public void processRequest(WydotTim wydotTim, TimType timType, String startDateT iTim.setDirection("I"); dTim.setDirection("D"); // I - expireReduceCreateSendTims(iTim, timType, startDateTime, endDateTime, pk, content, frameType); + expireReduceCreateSendTims(iTim, timType, startDateTime, endDateTime, pk, content, + frameType); // D - expireReduceCreateSendTims(dTim, timType, startDateTime, endDateTime, pk, content, frameType); + expireReduceCreateSendTims(dTim, timType, startDateTime, endDateTime, pk, content, + frameType); } else { - expireReduceCreateSendTims(wydotTim, timType, startDateTime, endDateTime, pk, content, frameType); + expireReduceCreateSendTims(wydotTim, timType, startDateTime, endDateTime, pk, content, + frameType); } } public TimType getTimType(String timTypeName) { - if (timType != null && timType.getType() == timTypeName) { + if (timType != null && Objects.equals(timType.getType(), timTypeName)) { return timType; } else { // get tim type - timType = getTimTypes().stream().filter(x -> x.getType().equals(timTypeName)).findFirst().orElse(null); + timType = + getTimTypes().stream().filter(x -> x.getType().equals(timTypeName)).findFirst() + .orElse(null); return timType; } } public List getTimTypes() { - if (timTypes != null) + if (timTypes != null) { return timTypes; - else { + } else { timTypes = timTypeService.selectAll(); return timTypes; } } - protected void expireReduceCreateSendTims(WydotTim wydotTim, TimType timType, String startDateTime, - String endDateTime, - Integer pk, ContentEnum content, TravelerInfoType frameType) { + protected void expireReduceCreateSendTims(WydotTim wydotTim, TimType timType, + String startDateTime, String endDateTime, Integer pk, + ContentEnum content, TravelerInfoType frameType) { // Clear any existing TIMs with the same client id Long timTypeId = timType != null ? timType.getTimTypeId() : null; - var existingTims = activeTimService.getActiveTimsByClientIdDirection(wydotTim.getClientId(), timTypeId, + var existingTims = + activeTimService.getActiveTimsByClientIdDirection(wydotTim.getClientId(), timTypeId, wydotTim.getDirection()); // Expire existing tims - List existingTimIds = new ArrayList(); + List existingTimIds = new ArrayList<>(); for (ActiveTim existingTim : existingTims) { existingTimIds.add(existingTim.getActiveTimId()); } @@ -661,22 +710,36 @@ protected void expireReduceCreateSendTims(WydotTim wydotTim, TimType timType, St Milepost firstPoint = milepostsAll.get(0); Milepost secondPoint = milepostsAll.get(1); - var anchor = getAnchorPoint(firstPoint, secondPoint); + Milepost anchor; + try { + Coordinate anchorCoordinate = utility.calculateAnchorCoordinate(firstPoint, secondPoint); + anchor = new Milepost(null, firstPoint.getMilepost(), firstPoint.getDirection(), anchorCoordinate.getLatitude(), + anchorCoordinate.getLongitude()); + } catch (Utility.IdenticalPointsException e) { + anchor = identicalPointsExceptionHandler.recover(milepostsAll); + if (anchor == null) { + log.error("Unable to recover from identical points exception for active TIM."); + return; + } + + } var reducedMileposts = milepostReduction.applyMilepostReductionAlgorithm(milepostsAll, - configuration.getPathDistanceLimit()); + configuration.getPathDistanceLimit()); - createSendTims(wydotTim, timType, startDateTime, endDateTime, pk, content, frameType, milepostsAll, - reducedMileposts, anchor); + createSendTims(wydotTim, timType, startDateTime, endDateTime, pk, content, frameType, + milepostsAll, reducedMileposts, anchor); } // creates a TIM and sends it to RSUs and Satellite - protected void createSendTims(WydotTim wydotTim, TimType timType, String startDateTime, String endDateTime, - Integer pk, ContentEnum content, TravelerInfoType frameType, List allMileposts, - List reducedMileposts, Milepost anchor) { + protected void createSendTims(WydotTim wydotTim, TimType timType, String startDateTime, + String endDateTime, Integer pk, ContentEnum content, + TravelerInfoType frameType, List allMileposts, + List reducedMileposts, Milepost anchor) { // create TIM - WydotTravelerInputData timToSend = wydotTimService.createTim(wydotTim, timType.getType(), startDateTime, - endDateTime, content, frameType, allMileposts, reducedMileposts, anchor); + WydotTravelerInputData timToSend = + wydotTimService.createTim(wydotTim, timType.getType(), startDateTime, endDateTime, + content, frameType, allMileposts, reducedMileposts, anchor); if (timToSend == null) { return; @@ -694,29 +757,13 @@ protected void createSendTims(WydotTim wydotTim, TimType timType, String startDa if (Arrays.asList(configuration.getRsuRoutes()).contains(wydotTim.getRoute())) { // send TIM to RSUs - wydotTimService.sendTimToRsus(wydotTim, timToSend, regionNamePrev, timType, pk, endDateTime, endPoint); + wydotTimService.sendTimToRsus(wydotTim, timToSend, regionNamePrev, timType, pk, + endDateTime, endPoint); } // send TIM to SDW // remove rsus from TIM timToSend.getRequest().setRsus(null); - wydotTimService.sendTimToSDW(wydotTim, timToSend, regionNamePrev, timType, pk, endPoint, reducedMileposts); - } - - /** - * This method returns the anchor point for the given mileposts. - * - * @param firstPoint The first milepost. - * @param secondPoint The second milepost. - * @return The anchor point as a Milepost. - */ - private Milepost getAnchorPoint(Milepost firstPoint, Milepost secondPoint) { - Coordinate anchorCoordinate = utility.calculateAnchorCoordinate(firstPoint, secondPoint); - - Milepost anchor = new Milepost(); - anchor.setLatitude(anchorCoordinate.getLatitude()); - anchor.setLongitude(anchorCoordinate.getLongitude()); - anchor.setMilepost(firstPoint.getMilepost()); - anchor.setDirection(firstPoint.getDirection()); - return anchor; + wydotTimService.sendTimToSDW(wydotTim, timToSend, regionNamePrev, timType, pk, endPoint, + reducedMileposts); } } \ No newline at end of file diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimBowrController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimBowrController.java index fc392c927..c7ed16e7b 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimBowrController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimBowrController.java @@ -1,5 +1,6 @@ package com.trihydro.odewrapper.controller; +import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; import java.util.ArrayList; import java.util.List; @@ -43,9 +44,9 @@ public class WydotTimBowrController extends WydotTimBaseController { public WydotTimBowrController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, - TimGenerationHelper _timGenerationHelper) { + TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { super(_basicConfiguration, _wydotTimService, _timTypeService, _setItisCodes, _activeTimService, - _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper); + _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler); } @RequestMapping(value = "/create-or-update-bowr-tim", method = RequestMethod.POST, headers = "Accept=application/json") diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimCcController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimCcController.java index aa9d91f41..9fc7ea454 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimCcController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimCcController.java @@ -1,5 +1,6 @@ package com.trihydro.odewrapper.controller; +import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -44,9 +45,9 @@ public class WydotTimCcController extends WydotTimBaseController { public WydotTimCcController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, - TimGenerationHelper _timGenerationHelper) { + TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { super(_basicConfiguration, _wydotTimService, _timTypeService, _setItisCodes, _activeTimService, - _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper); + _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler); } @RequestMapping(value = "/cc-tim", method = RequestMethod.POST, headers = "Accept=application/json") diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimIncidentController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimIncidentController.java index ad72b610a..6efd15d6b 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimIncidentController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimIncidentController.java @@ -1,10 +1,10 @@ package com.trihydro.odewrapper.controller; +import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; -import java.util.Date; import java.util.List; import com.trihydro.library.helpers.MilepostReduction; import com.trihydro.library.helpers.TimGenerationHelper; @@ -21,6 +21,7 @@ import com.trihydro.odewrapper.model.TimIncidentList; import com.trihydro.odewrapper.model.WydotTimIncident; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -37,140 +38,114 @@ @CrossOrigin @RestController @Api(description = "Incidents") +@Slf4j public class WydotTimIncidentController extends WydotTimBaseController { - private final String type = "I"; + private final String type = "I"; - @Autowired - public WydotTimIncidentController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, - TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, - RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, - TimGenerationHelper _timGenerationHelper) { - super(_basicConfiguration, _wydotTimService, _timTypeService, _setItisCodes, _activeTimService, - _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper); - } - - @RequestMapping(value = "/incident-tim", method = RequestMethod.POST, headers = "Accept=application/json") - public ResponseEntity createIncidentTim(@RequestBody TimIncidentList timIncidentList) { - - DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); - Date date = new Date(); - - utility.logWithDate(dateFormat.format(date) + " - Create Incident TIM", this.getClass()); - String post = gson.toJson(timIncidentList); - utility.logWithDate(post.toString(), this.getClass()); + @Autowired + public WydotTimIncidentController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, TimTypeService _timTypeService, + SetItisCodes _setItisCodes, ActiveTimService _activeTimService, RestTemplateProvider _restTemplateProvider, + MilepostReduction _milepostReduction, Utility _utility, TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { + super(_basicConfiguration, _wydotTimService, _timTypeService, _setItisCodes, _activeTimService, _restTemplateProvider, _milepostReduction, + _utility, _timGenerationHelper, identicalPointsExceptionHandler); + } - List timsToSend = new ArrayList(); + @RequestMapping(value = "/incident-tim", method = RequestMethod.POST, headers = "Accept=application/json") + public ResponseEntity createIncidentTim(@RequestBody TimIncidentList timIncidentList) { + log.info("Create Incident TIM"); + String post = gson.toJson(timIncidentList); + log.info(post); - List resultList = new ArrayList(); - ControllerResult resultTim = null; + List timsToSend = new ArrayList<>(); - // build TIM - for (WydotTimIncident wydotTim : timIncidentList.getTimIncidentList()) { + List resultList = new ArrayList<>(); + ControllerResult resultTim; - resultTim = validateInputIncident(wydotTim); + // build TIM + for (WydotTimIncident wydotTim : timIncidentList.getTimIncidentList()) { - if (resultTim.getResultMessages().size() > 0) { - resultList.add(resultTim); - continue; - } + resultTim = validateInputIncident(wydotTim); - // make tims - timsToSend.add(wydotTim); + if (!resultTim.getResultMessages().isEmpty()) { + resultList.add(resultTim); + continue; + } - resultTim.getResultMessages().add("success"); - resultList.add(resultTim); - } + // make tims + timsToSend.add(wydotTim); - makeTimsAsync(timsToSend); - - String responseMessage = gson.toJson(resultList); - return ResponseEntity.status(HttpStatus.OK).body(responseMessage); + resultTim.getResultMessages().add("success"); + resultList.add(resultTim); } - @RequestMapping(value = "/incident-tim", method = RequestMethod.PUT, headers = "Accept=application/json") - public ResponseEntity updateIncidentTim(@RequestBody TimIncidentList timIncidentList) { - - DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); - Date date = new Date(); - - utility.logWithDate(dateFormat.format(date) + " - Update Incident TIM", this.getClass()); - String post = gson.toJson(timIncidentList); - utility.logWithDate(post.toString(), this.getClass()); - - List resultList = new ArrayList(); - ControllerResult resultTim = null; - List timsToSend = new ArrayList(); - - // delete TIMs - for (WydotTimIncident wydotTim : timIncidentList.getTimIncidentList()) { - - resultTim = validateInputIncident(wydotTim); - - if (resultTim.getResultMessages().size() > 0) { - resultList.add(resultTim); - continue; - } - - // make tims - timsToSend.add(wydotTim); - - resultTim.getResultMessages().add("success"); - resultList.add(resultTim); - } - if (timsToSend.size() > 0) { - // make tims, expire existing ones, and send them - makeTimsAsync(timsToSend); - } - - String responseMessage = gson.toJson(resultList); - return ResponseEntity.status(HttpStatus.OK).body(responseMessage); + makeTimsAsync(timsToSend); + + String responseMessage = gson.toJson(resultList); + return ResponseEntity.status(HttpStatus.OK).body(responseMessage); + } + + @RequestMapping(value = "/incident-tim", method = RequestMethod.PUT, headers = "Accept=application/json") + public ResponseEntity updateIncidentTim(@RequestBody TimIncidentList timIncidentList) { + log.info("Update Incident TIM"); + String post = gson.toJson(timIncidentList); + log.info(post); + + List resultList = new ArrayList<>(); + ControllerResult resultTim; + List timsToSend = new ArrayList<>(); + + // delete TIMs + for (WydotTimIncident wydotTim : timIncidentList.getTimIncidentList()) { + resultTim = validateInputIncident(wydotTim); + if (!resultTim.getResultMessages().isEmpty()) { + resultList.add(resultTim); + continue; + } + // make tims + timsToSend.add(wydotTim); + resultTim.getResultMessages().add("success"); + resultList.add(resultTim); } - public void makeTimsAsync(List wydotTims) { - - new Thread(new Runnable() { - public void run() { - var startTime = getStartTime(); - for (WydotTimIncident wydotTim : wydotTims) { - // set route - wydotTim.setRoute(wydotTim.getHighway()); - processRequest(wydotTim, getTimType(type), startTime, null, wydotTim.getPk(), ContentEnum.advisory, - TravelerInfoType.advisory); - } - } - }).start(); - } - - @RequestMapping(value = "/incident-tim/{incidentId}", method = RequestMethod.DELETE, headers = "Accept=application/json") - public ResponseEntity deleteIncidentTim(@PathVariable String incidentId) { - - DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); - Date date = new Date(); - - utility.logWithDate(dateFormat.format(date) + " - Delete Incident TIM", this.getClass()); - - // expire and clear TIM - wydotTimService.clearTimsById("I", incidentId, null); - - String responseMessage = "success"; - return ResponseEntity.status(HttpStatus.OK).body(responseMessage); - } - - @RequestMapping(value = "/incident-tim", method = RequestMethod.GET, headers = "Accept=application/json") - public Collection getIncidentTims() { - - // get active TIMs - List activeTims = wydotTimService.selectTimsByType("I"); - - return activeTims; - } - - @RequestMapping(value = "/incident-tim/{incidentId}", method = RequestMethod.GET, headers = "Accept=application/json") - public Collection getIncidentTimById(@PathVariable String incidentId) { - - // get active TIMs - List activeTims = wydotTimService.selectTimByClientId("I", incidentId); - return activeTims; + if (!timsToSend.isEmpty()) { + // make tims, expire existing ones, and send them + makeTimsAsync(timsToSend); } + String responseMessage = gson.toJson(resultList); + return ResponseEntity.status(HttpStatus.OK).body(responseMessage); + } + + public void makeTimsAsync(List wydotTims) { + new Thread(() -> { + var startTime = getStartTime(); + for (WydotTimIncident wydotTim : wydotTims) { + // set route + wydotTim.setRoute(wydotTim.getHighway()); + processRequest(wydotTim, getTimType(type), startTime, null, wydotTim.getPk(), ContentEnum.advisory, TravelerInfoType.advisory); + } + }).start(); + } + + @RequestMapping(value = "/incident-tim/{incidentId}", method = RequestMethod.DELETE, headers = "Accept=application/json") + public ResponseEntity deleteIncidentTim(@PathVariable String incidentId) { + log.info("Delete Incident TIM"); + + // expire and clear TIM + wydotTimService.clearTimsById("I", incidentId, null); + String responseMessage = "success"; + return ResponseEntity.status(HttpStatus.OK).body(responseMessage); + } + + @RequestMapping(value = "/incident-tim", method = RequestMethod.GET, headers = "Accept=application/json") + public Collection getIncidentTims() { + // get active TIMs + return wydotTimService.selectTimsByType("I"); + } + + @RequestMapping(value = "/incident-tim/{incidentId}", method = RequestMethod.GET, headers = "Accept=application/json") + public Collection getIncidentTimById(@PathVariable String incidentId) { + // get active TIMs + return wydotTimService.selectTimByClientId("I", incidentId); + } } diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimParkingController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimParkingController.java index e2a46b08d..b7b9ddc26 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimParkingController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimParkingController.java @@ -1,5 +1,6 @@ package com.trihydro.odewrapper.controller; +import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -46,9 +47,9 @@ public class WydotTimParkingController extends WydotTimBaseController { public WydotTimParkingController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, - TimGenerationHelper _timGenerationHelper) { + TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { super(_basicConfiguration, _wydotTimService, _timTypeService, _setItisCodes, _activeTimService, - _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper); + _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler); } @RequestMapping(value = "/parking-tim", method = RequestMethod.POST, headers = "Accept=application/json") diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimRcController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimRcController.java index 9e687fb40..86ad03ba8 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimRcController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimRcController.java @@ -1,5 +1,6 @@ package com.trihydro.odewrapper.controller; +import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -46,9 +47,9 @@ public class WydotTimRcController extends WydotTimBaseController { public WydotTimRcController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, - TimGenerationHelper _timGenerationHelper) { + TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { super(_basicConfiguration, _wydotTimService, _timTypeService, _setItisCodes, _activeTimService, - _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper); + _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler); configuration = _basicConfiguration; } diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimRwController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimRwController.java index 9754c98c3..7c014d700 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimRwController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimRwController.java @@ -1,5 +1,6 @@ package com.trihydro.odewrapper.controller; +import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; @@ -52,9 +53,9 @@ public class WydotTimRwController extends WydotTimBaseController { public WydotTimRwController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, - TimGenerationHelper _timGenerationHelper) { + TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { super(_basicConfiguration, _wydotTimService, _timTypeService, _setItisCodes, _activeTimService, - _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper); + _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler); } @RequestMapping(value = "/rw-tim", method = RequestMethod.POST, headers = "Accept=application/json") diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimVslController.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimVslController.java index 2a0854462..987a8d78a 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimVslController.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/controller/WydotTimVslController.java @@ -1,5 +1,6 @@ package com.trihydro.odewrapper.controller; +import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -46,9 +47,9 @@ public class WydotTimVslController extends WydotTimBaseController { public WydotTimVslController(BasicConfiguration _basicConfiguration, WydotTimService _wydotTimService, TimTypeService _timTypeService, SetItisCodes _setItisCodes, ActiveTimService _activeTimService, RestTemplateProvider _restTemplateProvider, MilepostReduction _milepostReduction, Utility _utility, - TimGenerationHelper _timGenerationHelper) { + TimGenerationHelper _timGenerationHelper, IdenticalPointsExceptionHandler identicalPointsExceptionHandler) { super(_basicConfiguration, _wydotTimService, _timTypeService, _setItisCodes, _activeTimService, - _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper); + _restTemplateProvider, _milepostReduction, _utility, _timGenerationHelper, identicalPointsExceptionHandler); } @RequestMapping(value = "/vsl-tim", method = RequestMethod.POST, headers = "Accept=application/json") diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/helpers/SetItisCodes.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/helpers/SetItisCodes.java index 54b0dca72..062daa1a3 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/helpers/SetItisCodes.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/helpers/SetItisCodes.java @@ -3,7 +3,6 @@ import java.util.ArrayList; import java.util.List; -import com.trihydro.library.helpers.Utility; import com.trihydro.library.model.CustomItisEnum; import com.trihydro.library.model.IncidentChoice; import com.trihydro.library.model.ItisCode; @@ -16,352 +15,351 @@ import com.trihydro.odewrapper.model.WydotTimRc; import com.trihydro.odewrapper.model.WydotTimVsl; -import org.apache.commons.lang3.StringUtils; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component +@Slf4j public class SetItisCodes { - private List incidentProblems; - private List incidentEffects; - private List incidentActions; - private IncidentChoicesService incidentChoicesService; - private ItisCodeService itisCodeService; - private Utility utility; - - private List itisCodes; - - @Autowired - public void InjectDependencies(ItisCodeService _itisCodeService, IncidentChoicesService _incidentChoicesService, - Utility _utility) { - itisCodeService = _itisCodeService; - incidentChoicesService = _incidentChoicesService; - utility = _utility; + private final IncidentChoicesService incidentChoicesService; + private final ItisCodeService itisCodeService; + + private List incidentProblems; + private List incidentEffects; + private List incidentActions; + + private List itisCodes; + + @Autowired + public SetItisCodes(ItisCodeService _itisCodeService, IncidentChoicesService _incidentChoicesService) { + itisCodeService = _itisCodeService; + incidentChoicesService = _incidentChoicesService; + } + + public List getItisCodes() { + if (itisCodes != null) { + return itisCodes; + } else { + itisCodes = itisCodeService.selectAll(); + return itisCodes; } + } - public List getItisCodes() { - if (itisCodes != null) - return itisCodes; - else { - itisCodes = itisCodeService.selectAll(); - return itisCodes; - } - } - - public List setItisCodesFromAdvisoryArray(WydotTimRc wydotTim) { + public List setItisCodesFromAdvisoryArray(WydotTimRc wydotTim) { - // check to see if code exists + // check to see if code exists - List items = new ArrayList(); - for (Integer item : wydotTim.getAdvisory()) { + List items = new ArrayList<>(); + for (Integer item : wydotTim.getAdvisory()) { - ItisCode code = getItisCodes().stream().filter(x -> x.getItisCode().equals(item)).findFirst().orElse(null); + getItisCodes().stream().filter(x -> x.getItisCode().equals(item)).findFirst().ifPresent(code -> items.add(item.toString())); - if (code != null) - items.add(item.toString()); - } - return items; } + return items; + } - public List setItisCodesRc(WydotTimRc wydotTim) { - - List items = new ArrayList(); - - if (wydotTim.getAdvisory() == null) { - return items; - } - - ItisCode code = null; - - for (Integer item : wydotTim.getAdvisory()) { + public List setItisCodesRc(WydotTimRc wydotTim) { - var alphaItis = getCustomAlphabetic(item); - if (alphaItis != null) { - items.add(alphaItis); - continue; - } - // map "closed" itis code - if (item == 769) { - code = getItisCodes().stream().filter(x -> x.getItisCode().equals(770)).findFirst().orElse(null); - } else { - code = getItisCodes().stream().filter(x -> x.getItisCode().equals(item)).findFirst().orElse(null); - } + List items = new ArrayList<>(); - if (code != null) - items.add(code.getItisCode().toString()); - } - - return items; + if (wydotTim.getAdvisory() == null) { + return items; } - public String getCustomAlphabetic(Integer itisCode) { - String text = null; - var en = CustomItisEnum.valueOf(itisCode); - if (en != null) { - text = en.getStringValue(); - } - return text; + ItisCode code; + + for (Integer item : wydotTim.getAdvisory()) { + + var alphaItis = getCustomAlphabetic(item); + if (alphaItis != null) { + items.add(alphaItis); + continue; + } + // map "closed" itis code + if (item == 769) { + code = getItisCodes().stream().filter(x -> x.getItisCode().equals(770)).findFirst().orElse(null); + } else { + code = getItisCodes().stream().filter(x -> x.getItisCode().equals(item)).findFirst().orElse(null); + } + + if (code != null) { + items.add(code.getItisCode().toString()); + } } - public List setItisCodesVsl(WydotTimVsl wydotTim) { + return items; + } - List items = new ArrayList(); - - // speed limit itis code - ItisCode speedLimit = getItisCodes().stream().filter(x -> x.getDescription().equals("speed limit")).findFirst() - .orElse(null); - if (speedLimit != null) { - items.add(speedLimit.getItisCode().toString()); - } - - // number e.g 50, convert to ITIS code - Integer speed = wydotTim.getSpeed() + 12544; - items.add(speed.toString()); - - // mph itis code - ItisCode mph = getItisCodes().stream().filter(x -> x.getDescription().equals("mph")).findFirst().orElse(null); - if (mph != null) { - items.add(mph.getItisCode().toString()); - } - - return items; + public String getCustomAlphabetic(Integer itisCode) { + String text = null; + var en = CustomItisEnum.valueOf(itisCode); + if (en != null) { + text = en.getStringValue(); } + return text; + } - public List setItisCodesParking(WydotTimParking wydotTim) { - - // check to see if code exists - List items = new ArrayList(); - - ItisCode code = getItisCodes().stream().filter(x -> x.getItisCode().equals(wydotTim.getAvailability())) - .findFirst().orElse(null); - - utility.logWithDate("Availablity : " + wydotTim.getAvailability(), this.getClass()); - utility.logWithDate("Exit : " + wydotTim.getExit(), this.getClass()); - - if (code != null) - items.add(wydotTim.getAvailability().toString()); - - // for parking TIM, content=exitService, and includes additional itis codes - // depending on if rest area or exit number - if (wydotTim.getExit() != null) { - // if exit, the exit number should be a text value. - // This has some strange implications as seen here - // https://github.com/usdot-jpo-ode/jpo-ode/blob/540b79f1697f4d6464e8c4b8491666ec9cf08d8d/jpo-ode-plugins/src/main/java/us/dot/its/jpo/ode/plugin/j2735/builders/TravelerMessageFromHumanToAsnConverter.java#L337 - // the ODE translates a text value only if we start with a single quote to - // denote this. No ending quote is used - items.add("11794");// Exit Number - if (wydotTim.getExit().toLowerCase().equals("turnout") - || wydotTim.getExit().toLowerCase().equals("parking")) { - items.add("'" + String.valueOf(((int) Math.round(wydotTim.getMileMarker())))); - } else { - items.add("'" + wydotTim.getExit()); - } - } else { - items.add("7986");// Rest Area - utility.logWithDate("rest area", this.getClass()); - } + public List setItisCodesVsl(WydotTimVsl wydotTim) { - return items; - } + List items = new ArrayList<>(); - public List splitExitNumberFromLetter(String exit) { - - List list = new ArrayList(); - String exitNumber = ""; - String exitLetter = ""; - for (int i = 0; i < exit.length(); i++) { - if (StringUtils.isNumeric(String.valueOf(exit.charAt(i)))) { - exitNumber += exit.charAt(i); - } else { - exitLetter += exit.charAt(i); - } - } + // speed limit itis code + getItisCodes().stream().filter(x -> x.getDescription().equals("speed limit")).findFirst() + .ifPresent(speedLimit -> items.add(speedLimit.getItisCode().toString())); - list.add(exitNumber); - if (exitLetter.length() > 0) - list.add(exitLetter); + // number e.g 50, convert to ITIS code + int speed = wydotTim.getSpeed() + 12544; + items.add(Integer.toString(speed)); - return list; - } + // mph itis code + getItisCodes().stream().filter(x -> x.getDescription().equals("mph")).findFirst().ifPresent(mph -> items.add(mph.getItisCode().toString())); - public List setItisCodesFromAvailability(WydotTimParking wydotTim) { + return items; + } - // check to see if code exists - List items = new ArrayList(); + public List setItisCodesParking(WydotTimParking wydotTim) { - utility.logWithDate("availability:" + wydotTim.getAvailability(), this.getClass()); + // check to see if code exists + List items = new ArrayList<>(); - ItisCode code = getItisCodes().stream().filter(x -> x.getItisCode().equals(wydotTim.getAvailability())) - .findFirst().orElse(null); + ItisCode code = getItisCodes().stream().filter(x -> x.getItisCode().equals(wydotTim.getAvailability())).findFirst().orElse(null); - if (code != null) - items.add(wydotTim.getAvailability().toString()); + log.info("Availablity : {}", wydotTim.getAvailability()); + log.info("Exit : {}", wydotTim.getExit()); - if (wydotTim.getExit() != null) { - items.add("11794"); - items.add(wydotTim.getExit()); - } else { - items.add("7986"); - } - - return items; + if (code != null) { + items.add(wydotTim.getAvailability().toString()); } - public List setItisCodesIncident(WydotTimIncident wydotTim) { - List items = new ArrayList(); - - // action - IncidentChoice incidentAction = getIncidentActions().stream() - .filter(x -> x.getCode().equals(wydotTim.getAction())).findFirst().orElse(null); + // for parking TIM, content=exitService, and includes additional itis codes + // depending on if rest area or exit number + if (wydotTim.getExit() != null) { + // if exit, the exit number should be a text value. + // This has some strange implications as seen here + // https://github.com/usdot-jpo-ode/jpo-ode/blob/540b79f1697f4d6464e8c4b8491666ec9cf08d8d/jpo-ode-plugins/src/main/java/us/dot/its/jpo/ode/plugin/j2735/builders/TravelerMessageFromHumanToAsnConverter.java#L337 + // the ODE translates a text value only if we start with a single quote to + // denote this. No ending quote is used + items.add("11794");// Exit Number + if (wydotTim.getExit().equalsIgnoreCase("turnout") || wydotTim.getExit().equalsIgnoreCase("parking")) { + items.add("'" + (int) Math.round(wydotTim.getMileMarker())); + } else { + items.add("'" + wydotTim.getExit()); + } + } else { + items.add("7986");// Rest Area + log.info("rest area"); + } - // if action is not null and action itis code exists - if (incidentAction != null && incidentAction.getItisCodeId() != null) { - ItisCode actionItisCode = getItisCodes().stream() - .filter(x -> x.getItisCodeId().equals(incidentAction.getItisCodeId())).findFirst().orElse(null); - if (actionItisCode != null) { - items.add(actionItisCode.getItisCode().toString()); - } - } + return items; + } - // effect - IncidentChoice incidentEffect = getIncidentEffects().stream() - .filter(x -> x.getCode().equals(wydotTim.getEffect())).findFirst().orElse(null); - - // if effect is not null and effect itis code exists - if (incidentEffect != null && incidentEffect.getItisCodeId() != null) { - ItisCode effectItisCode = getItisCodes().stream() - .filter(x -> x.getItisCodeId().equals(incidentEffect.getItisCodeId())).findFirst().orElse(null); - if (effectItisCode != null) { - items.add(effectItisCode.getItisCode().toString()); - } - } + public List setItisCodesIncident(WydotTimIncident wydotTim) { + List items = new ArrayList<>(); - // problem - IncidentChoice incidentProblem = getIncidentProblems().stream() - .filter(x -> x.getCode().equals(wydotTim.getProblem())).findFirst().orElse(null); - - // if problem is not null and problem itis code exists - if (incidentProblem != null && incidentProblem.getItisCodeId() != null) { - ItisCode problemItisCode = getItisCodes().stream() - .filter(x -> x.getItisCodeId().equals(incidentProblem.getItisCodeId())).findFirst().orElse(null); - if (problemItisCode != null) { - items.add(problemItisCode.getItisCode().toString()); - } - } + // action + IncidentChoice incidentAction = getIncidentActions().stream().filter(x -> x.getCode().equals(wydotTim.getAction())).findFirst().orElse(null); - if (items.size() == 0) - items.add("531");// 531 is "Incident" - - return items; + // if action is not null and action itis code exists + if (incidentAction != null && incidentAction.getItisCodeId() != null) { + getItisCodes().stream().filter(x -> x.getItisCodeId().equals(incidentAction.getItisCodeId())).findFirst() + .ifPresent(actionItisCode -> items.add(actionItisCode.getItisCode().toString())); } - public List getIncidentProblems() { - if (incidentProblems != null) - return incidentProblems; - else { - incidentProblems = incidentChoicesService.selectAllIncidentProblems(); - return incidentProblems; - } - } + // effect + IncidentChoice incidentEffect = getIncidentEffects().stream().filter(x -> x.getCode().equals(wydotTim.getEffect())).findFirst().orElse(null); - public List getIncidentEffects() { - if (incidentEffects != null) - return incidentEffects; - else { - incidentEffects = incidentChoicesService.selectAllIncidentEffects(); - return incidentEffects; - } + // if effect is not null and effect itis code exists + if (incidentEffect != null && incidentEffect.getItisCodeId() != null) { + getItisCodes().stream().filter(x -> x.getItisCodeId().equals(incidentEffect.getItisCodeId())).findFirst() + .ifPresent(effectItisCode -> items.add(effectItisCode.getItisCode().toString())); } - public List getIncidentActions() { - if (incidentActions != null) - return incidentActions; - else { - incidentActions = incidentChoicesService.selectAllIncidentActions(); - return incidentActions; + if (wydotTim.getProblem() != null && !wydotTim.getProblem().equals("other")) { + // Retrieve the matching incident problem based on the provided code + IncidentChoice incidentProblem = + getIncidentProblems().stream().filter(problem -> problem.getCode().equals(wydotTim.getProblem())).findFirst().orElse(null); + + // Add the ITIS code if the incident problem exists and has a valid ITIS code ID + if (incidentProblem != null) { + Integer itisCodeId = incidentProblem.getItisCodeId(); + if (itisCodeId != null) { + getItisCodes().stream().filter(code -> code.getItisCodeId().equals(itisCodeId)).findFirst() + .ifPresent(problemItisCode -> items.add(problemItisCode.getItisCode().toString())); } + } + } else { + items.addAll(handleOtherIncidentProblem(wydotTim)); } - public List setItisCodesRw(WydotTim wydotTim) { - - List items = new ArrayList(); + // If no incident problem is provided, default to "Incident" (ITIS code 531) + if (items.isEmpty()) { + items.add("531"); // 531 is "Incident" + } - items.add("1025"); + return items; + } - return items; + private List handleOtherIncidentProblem(WydotTimIncident wydotTim) { + List items = new ArrayList<>(); + if (wydotTim.getProblemOtherText() == null) { + log.warn("problemOtherText is null for 'other' incident problem"); + return items; } + String problemOtherText = wydotTim.getProblemOtherText(); - public List setItisCodesBowr(WydotTimBowr tim) throws WeightNotSupportedException { - List itisCodes = new ArrayList(); - - int weightInPounds = tim.getData(); + if (!problemOtherText.contains("GVW")) { + log.error("Unsupported problemOtherText: {}", problemOtherText); + return items; + } - itisCodes.add("5127"); // Strong winds - itisCodes.add("2563"); // Truck restriction - itisCodes.add("2569"); // No high profile vehicles - itisCodes.add("7682"); // Below - itisCodes.add("2577"); // Gross-Weight-Limit - itisCodes.add(translateWeightToItisCode(weightInPounds)); // Weight, translated from pounds to ITIS code - itisCodes.add("8739"); // Pounds + // Extract the weight limit from the problemOtherText + String weightLimitInPounds = getWeightLimitFromProblemOtherText(problemOtherText); + if (weightLimitInPounds == null) { + log.warn("Weight limit not found in problemOtherText: {}", problemOtherText); + return items; + } - return itisCodes; + String weightLimitInItisCode = null; + try { + // Convert weight limit to ITIS code + weightLimitInItisCode = translateWeightToItisCode(Integer.parseInt(weightLimitInPounds)); + } catch (WeightNotSupportedException e) { + log.warn("Weight limit not supported: {}", weightLimitInPounds); + return items; } - /** - * This method translates the weight in pounds to its corresponding ITIS code. - * These are large number ITIS codes and do not abide by the standard translations used for other numbers such as mph. - * Supported weights are 20000 to 30000 in increments of 1000 and 30000 to 70000 in increments of 5000 - * @throws WeightNotSupportedException - */ - private String translateWeightToItisCode(int weightInPounds) throws WeightNotSupportedException { - switch(weightInPounds) { - case 20000: - return "11589"; - case 21000: - return "11590"; - case 22000: - return "11591"; - case 23000: - return "11592"; - case 24000: - return "11593"; - case 25000: - return "11594"; - case 26000: - return "11595"; - case 27000: - return "11596"; - case 28000: - return "11597"; - case 29000: - return "11598"; - case 30000: - return "11599"; - case 35000: - return "11600"; - case 40000: - return "11601"; - case 45000: - return "11602"; - case 50000: - return "11603"; - case 55000: - return "11604"; - case 60000: - return "11605"; - case 65000: - return "11606"; - case 70000: - return "11607"; - default: - throw new WeightNotSupportedException("Weight " + weightInPounds + " is not supported"); - } + items.add("2563"); // Truck restriction + items.add("2577"); // Gross-Weight-Limit + items.add(weightLimitInItisCode); // Weight limit in ITIS code + items.add("8739"); // Pounds + + return items; + } + + /** + * Given a problemOtherText string of the format "Weight limit of 60,000 GVW is in effect", + * return the weight limit (60000) in pounds as a string. + */ + private String getWeightLimitFromProblemOtherText(String problemOtherText) { + problemOtherText = problemOtherText.replaceAll(",", ""); + String[] parts = problemOtherText.split(" "); + for (String part : parts) { + if (part.matches("\\d{1,5}")) { // Match a number with 1 to 5 digits + return part; + } } - - public class WeightNotSupportedException extends Exception { - public WeightNotSupportedException(String message) { - super(message); - } + return null; // Return null if no weight limit found + } + + public List getIncidentProblems() { + if (incidentProblems != null) { + return incidentProblems; + } else { + incidentProblems = incidentChoicesService.selectAllIncidentProblems(); + return incidentProblems; + } + } + + public List getIncidentEffects() { + if (incidentEffects != null) { + return incidentEffects; + } else { + incidentEffects = incidentChoicesService.selectAllIncidentEffects(); + return incidentEffects; + } + } + + public List getIncidentActions() { + if (incidentActions != null) { + return incidentActions; + } else { + incidentActions = incidentChoicesService.selectAllIncidentActions(); + return incidentActions; + } + } + + public List setItisCodesRw(WydotTim wydotTim) { + + List items = new ArrayList(); + + items.add("1025"); + + return items; + } + + public List setItisCodesBowr(WydotTimBowr tim) throws WeightNotSupportedException { + List itisCodes = new ArrayList<>(); + + int weightInPounds = tim.getData(); + + itisCodes.add("5127"); // Strong winds + itisCodes.add("2563"); // Truck restriction + itisCodes.add("2569"); // No high profile vehicles + itisCodes.add("7682"); // Below + itisCodes.add("2577"); // Gross-Weight-Limit + itisCodes.add(translateWeightToItisCode(weightInPounds)); // Weight, translated from pounds to ITIS code + itisCodes.add("8739"); // Pounds + + return itisCodes; + } + + /** + * This method translates the weight in pounds to its corresponding ITIS code. + * These are large number ITIS codes and do not abide by the standard translations used for other numbers such as mph. + * Supported weights are 20000 to 30000 in increments of 1000 and 30000 to 70000 in increments of 5000 + * + * @throws WeightNotSupportedException if the weight is not supported + */ + private String translateWeightToItisCode(int weightInPounds) throws WeightNotSupportedException { + switch (weightInPounds) { + case 20000: + return "11589"; + case 21000: + return "11590"; + case 22000: + return "11591"; + case 23000: + return "11592"; + case 24000: + return "11593"; + case 25000: + return "11594"; + case 26000: + return "11595"; + case 27000: + return "11596"; + case 28000: + return "11597"; + case 29000: + return "11598"; + case 30000: + return "11599"; + case 35000: + return "11600"; + case 40000: + return "11601"; + case 45000: + return "11602"; + case 50000: + return "11603"; + case 55000: + return "11604"; + case 60000: + return "11605"; + case 65000: + return "11606"; + case 70000: + return "11607"; + default: + throw new WeightNotSupportedException("Weight " + weightInPounds + " is not supported"); + } + } + + public static class WeightNotSupportedException extends Exception { + public WeightNotSupportedException(String message) { + super(message); } + } } \ No newline at end of file diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/model/WydotTimIncident.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/model/WydotTimIncident.java index a4e3867d9..a6492f68c 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/model/WydotTimIncident.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/model/WydotTimIncident.java @@ -10,99 +10,114 @@ public class WydotTimIncident extends WydotTim { - @ApiModelProperty(value = "Expected values are mudslide, livestock, avalanche, avalancheControl, landslide, wildfire, signInstall, mowing") - private String problem; - private String effect; - - @ApiModelProperty(value = "Expected values are caution, delays, stop") - private String action; - @ApiModelProperty(required = true) - private String incidentId; - @ApiModelProperty(required = true) - private String highway; - - private Integer pk; - @ApiModelProperty(hidden = true) - private transient String clientId; - @ApiModelProperty(hidden = true) - private transient String route; - @ApiModelProperty(hidden = true) - private transient List itisCodes; - - @ApiModelProperty(value = "Optional. If not provided, a TIM will be generated extending 1 mile upstream from the startPoint", required = false) - private transient Coordinate endPoint; - - public WydotTimIncident() { - + @ApiModelProperty(value = "Expected values are mudslide, livestock, avalanche, avalancheControl, landslide, wildfire, signInstall, mowing") + private String problem; + private String effect; + + @ApiModelProperty(value = "Expected values are caution, delays, stop") + private String action; + @ApiModelProperty(required = true) + private String incidentId; + @ApiModelProperty(required = true) + private String highway; + + private Integer pk; + @ApiModelProperty(hidden = true) + private transient String clientId; + @ApiModelProperty(hidden = true) + private transient String route; + @ApiModelProperty(hidden = true) + private transient List itisCodes; + + @ApiModelProperty(value = "Optional. If not provided, a TIM will be generated extending 1 mile upstream from the startPoint", required = false) + private transient Coordinate endPoint; + + @ApiModelProperty() + private transient String problemOtherText; + + public WydotTimIncident() { + + } + + public WydotTimIncident(WydotTimIncident o) { + super(o); + this.problem = o.problem; + this.effect = o.effect; + this.action = o.action; + this.incidentId = o.incidentId; + this.highway = o.highway; + this.pk = o.pk; + this.clientId = o.clientId; + this.route = o.route; + + if (o.itisCodes != null) { + this.itisCodes = new ArrayList<>(o.itisCodes); } - - public WydotTimIncident(WydotTimIncident o) { - super(o); - this.problem = o.problem; - this.effect = o.effect; - this.action = o.action; - this.incidentId = o.incidentId; - this.highway = o.highway; - this.pk = o.pk; - this.clientId = o.clientId; - this.route = o.route; - - if (o.itisCodes != null) - this.itisCodes = new ArrayList<>(o.itisCodes); - if (o.endPoint != null) - this.endPoint = new Coordinate(o.endPoint.getLatitude(), o.endPoint.getLongitude()); + if (o.endPoint != null) { + this.endPoint = new Coordinate(o.endPoint.getLatitude(), o.endPoint.getLongitude()); } - @Override - public WydotTimIncident copy() { - return new WydotTimIncident(this); - } + this.problemOtherText = o.problemOtherText; + } - public Integer getPk() { - return this.pk; - } + @Override + public WydotTimIncident copy() { + return new WydotTimIncident(this); + } - public void setPk(Integer pk) { - this.pk = pk; - } + public Integer getPk() { + return this.pk; + } - public String getHighway() { - return this.highway; - } + public void setPk(Integer pk) { + this.pk = pk; + } - public void setHighway(String highway) { - this.highway = highway; - } + public String getHighway() { + return this.highway; + } - public String getIncidentId() { - return this.incidentId; - } + public void setHighway(String highway) { + this.highway = highway; + } - public void setIncidentId(String incidentId) { - this.incidentId = incidentId; - } + public String getIncidentId() { + return this.incidentId; + } - public String getProblem() { - return this.problem; - } + public void setIncidentId(String incidentId) { + this.incidentId = incidentId; + } - public void setProblem(String problem) { - this.problem = problem; - } + public String getProblem() { + return this.problem; + } - public String getEffect() { - return this.effect; - } + public void setProblem(String problem) { + this.problem = problem; + } - public void setEffect(String effect) { - this.effect = effect; - } + public String getEffect() { + return this.effect; + } - public String getAction() { - return this.action; - } + public void setEffect(String effect) { + this.effect = effect; + } - public void setAction(String action) { - this.action = action; - } + public String getAction() { + return this.action; + } + + public void setAction(String action) { + this.action = action; + } + + public String getProblemOtherText() { + return this.problemOtherText; + } + + public void setProblemOtherText(String problemOtherText) { + this.problemOtherText = problemOtherText; + } } \ No newline at end of file diff --git a/ode-wrapper/src/main/java/com/trihydro/odewrapper/spring/ApplicationConfig.java b/ode-wrapper/src/main/java/com/trihydro/odewrapper/spring/ApplicationConfig.java index 7c1318aa4..7a257292c 100644 --- a/ode-wrapper/src/main/java/com/trihydro/odewrapper/spring/ApplicationConfig.java +++ b/ode-wrapper/src/main/java/com/trihydro/odewrapper/spring/ApplicationConfig.java @@ -1,5 +1,6 @@ package com.trihydro.odewrapper.spring; +import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; import com.trihydro.library.helpers.CreateBaseTimUtil; import com.trihydro.library.helpers.EmailHelper; import com.trihydro.library.helpers.JavaMailSenderImplProvider; @@ -30,7 +31,9 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration -@Import({ ActiveTimService.class, +@Import( + { + ActiveTimService.class, ActiveTimHoldingService.class, CreateBaseTimUtil.class, DataFrameService.class, @@ -53,7 +56,10 @@ TimGenerationHelper.class, Utility.class, WydotTimService.class, - RegionNameTrimmer.class }) + RegionNameTrimmer.class, + IdenticalPointsExceptionHandler.class + } +) public class ApplicationConfig implements WebMvcConfigurer { public ApplicationConfig() { super(); diff --git a/ode-wrapper/src/test/java/com/trihydro/odewrapper/helpers/SetItisCodesTest.java b/ode-wrapper/src/test/java/com/trihydro/odewrapper/helpers/SetItisCodesTest.java index f8fffa1f4..abbafcb6a 100644 --- a/ode-wrapper/src/test/java/com/trihydro/odewrapper/helpers/SetItisCodesTest.java +++ b/ode-wrapper/src/test/java/com/trihydro/odewrapper/helpers/SetItisCodesTest.java @@ -1,7 +1,10 @@ package com.trihydro.odewrapper.helpers; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; +import com.trihydro.library.model.IncidentChoice; +import com.trihydro.odewrapper.model.WydotTimIncident; import java.util.ArrayList; import java.util.List; @@ -23,135 +26,256 @@ @ExtendWith(MockitoExtension.class) public class SetItisCodesTest { - @Mock - ItisCodeService mockItisCodeService; - @Mock - IncidentChoicesService mockIncidentChoicesService; - - @InjectMocks - SetItisCodes uut; - - public void setup() { - List itisCodes = new ArrayList<>(); - ItisCode code = new ItisCode(); - code.setItisCode(268);// speed limit - itisCodes.add(code); - - code = new ItisCode(); - code.setItisCode(770);// closed - itisCodes.add(code); - - code = new ItisCode(); - code.setItisCode(1309);// rockfall - itisCodes.add(code); - - code = new ItisCode(); - code.setItisCode(3084);// wildfire - itisCodes.add(code); - - code = new ItisCode(); - code.setItisCode(4868); // snow - itisCodes.add(code); - - code = new ItisCode(); - code.setItisCode(770); // closed - itisCodes.add(code); - - doReturn(itisCodes).when(mockItisCodeService).selectAll(); - } - - @Test - public void setItisCodesRc_numeric() { - // Arrange - setup(); - WydotTimRc tim = new WydotTimRc(); - Integer[] itisCodes = new Integer[2]; - itisCodes[0] = 4868; - itisCodes[1] = 1309; - tim.setAdvisory(itisCodes); - // Act - var result = uut.setItisCodesRc(tim); - - // Assert - Assertions.assertEquals(2, result.size()); - } - - @Test - public void setItisCodesRc_nonExistent() { - // Arrange - setup(); - WydotTimRc tim = new WydotTimRc(); - Integer[] itisCodes = new Integer[2]; - itisCodes[0] = 0; - itisCodes[1] = 13; - tim.setAdvisory(itisCodes); - // Act - var result = uut.setItisCodesRc(tim); - - // Assert - Assertions.assertEquals(0, result.size()); - } - - @Test - public void setItisCodesRc_translated() { - // Arrange - setup(); - WydotTimRc tim = new WydotTimRc(); - Integer[] itisCodes = new Integer[2]; - itisCodes[0] = 4868; - itisCodes[1] = 769; - tim.setAdvisory(itisCodes); - // Act - var result = uut.setItisCodesRc(tim); - - // Assert - Assertions.assertEquals(2, result.size()); - Assertions.assertTrue(result.contains("770")); - } - - @Test - public void setItisCodesRc_alphabetic() { - // Arrange - setup(); - WydotTimRc tim = new WydotTimRc(); - Integer[] itisCodes = new Integer[2]; - itisCodes[0] = 4868; - itisCodes[1] = CustomItisEnum.blowOver.getValue(); - tim.setAdvisory(itisCodes); - // Act - var result = uut.setItisCodesRc(tim); - - // Assert - Assertions.assertEquals(2, result.size()); - Assertions.assertTrue(result.contains("Extreme blow over risk")); - } - - @Test - public void setItisCodesBowr_SUCCESS() throws WeightNotSupportedException { - // Arrange - // calling setup() not necessary here - int weightInPounds = 20000; - String weightAsItisCode = "11589"; - WydotTimBowr tim = new WydotTimBowr(); - tim.setData(weightInPounds); - List expectedResult = List.of("5127", "2563", "2569", "7682", "2577", weightAsItisCode, "8739"); - - // Act - List result = uut.setItisCodesBowr(tim); - - // Assert - Assertions.assertEquals(expectedResult, result); - } - - @Test - public void setItisCodesBowr_FAILURE() throws WeightNotSupportedException { - // Arrange - // calling setup() not necessary here - int weightInPounds = 23456; - WydotTimBowr tim = new WydotTimBowr(); - tim.setData(weightInPounds); - - // Act & Assert - Assertions.assertThrows(WeightNotSupportedException.class, () -> uut.setItisCodesBowr(tim)); - } + @Mock + ItisCodeService mockItisCodeService; + @Mock + IncidentChoicesService mockIncidentChoicesService; + + @InjectMocks + SetItisCodes uut; + + public void setup() { + List itisCodes = new ArrayList<>(); + ItisCode code = new ItisCode(); + code.setItisCode(268);// speed limit + itisCodes.add(code); + + code = new ItisCode(); + code.setItisCode(770);// closed + itisCodes.add(code); + + code = new ItisCode(); + code.setItisCode(1309);// rockfall + itisCodes.add(code); + + code = new ItisCode(); + code.setItisCode(3084);// wildfire + itisCodes.add(code); + + code = new ItisCode(); + code.setItisCode(4868); // snow + itisCodes.add(code); + + code = new ItisCode(); + code.setItisCode(770); // closed + itisCodes.add(code); + + doReturn(itisCodes).when(mockItisCodeService).selectAll(); + } + + @Test + public void setItisCodesRc_numeric() { + // Arrange + setup(); + WydotTimRc tim = new WydotTimRc(); + Integer[] itisCodes = new Integer[2]; + itisCodes[0] = 4868; + itisCodes[1] = 1309; + tim.setAdvisory(itisCodes); + // Act + var result = uut.setItisCodesRc(tim); + + // Assert + Assertions.assertEquals(2, result.size()); + } + + @Test + public void setItisCodesRc_nonExistent() { + // Arrange + setup(); + WydotTimRc tim = new WydotTimRc(); + Integer[] itisCodes = new Integer[2]; + itisCodes[0] = 0; + itisCodes[1] = 13; + tim.setAdvisory(itisCodes); + // Act + var result = uut.setItisCodesRc(tim); + + // Assert + Assertions.assertEquals(0, result.size()); + } + + @Test + public void setItisCodesRc_translated() { + // Arrange + setup(); + WydotTimRc tim = new WydotTimRc(); + Integer[] itisCodes = new Integer[2]; + itisCodes[0] = 4868; + itisCodes[1] = 769; + tim.setAdvisory(itisCodes); + // Act + var result = uut.setItisCodesRc(tim); + + // Assert + Assertions.assertEquals(2, result.size()); + Assertions.assertTrue(result.contains("770")); + } + + @Test + public void setItisCodesRc_alphabetic() { + // Arrange + setup(); + WydotTimRc tim = new WydotTimRc(); + Integer[] itisCodes = new Integer[2]; + itisCodes[0] = 4868; + itisCodes[1] = CustomItisEnum.blowOver.getValue(); + tim.setAdvisory(itisCodes); + // Act + var result = uut.setItisCodesRc(tim); + + // Assert + Assertions.assertEquals(2, result.size()); + Assertions.assertTrue(result.contains("Extreme blow over risk")); + } + + @Test + public void setItisCodesBowr_SUCCESS() throws WeightNotSupportedException { + // Arrange + // calling setup() not necessary here + int weightInPounds = 20000; + String weightAsItisCode = "11589"; + WydotTimBowr tim = new WydotTimBowr(); + tim.setData(weightInPounds); + List expectedResult = List.of("5127", "2563", "2569", "7682", "2577", weightAsItisCode, "8739"); + + // Act + List result = uut.setItisCodesBowr(tim); + + // Assert + Assertions.assertEquals(expectedResult, result); + } + + @Test + public void setItisCodesBowr_FAILURE() { + // Arrange + // calling setup() not necessary here + int weightInPounds = 23456; + WydotTimBowr tim = new WydotTimBowr(); + tim.setData(weightInPounds); + + // Act & Assert + Assertions.assertThrows(WeightNotSupportedException.class, () -> uut.setItisCodesBowr(tim)); + } + + @Test + public void testSetItisCodesIncident_ReturnsDefaultIncidentCode() { + // Arrange + WydotTimIncident mockIncident = new WydotTimIncident(); // Mock incident object + List expectedCodes = List.of("531"); // Expected default incident code + + // Act + List result = uut.setItisCodesIncident(mockIncident); + + // Assert + Assertions.assertNotNull(result, "Resulting list should not be null."); + Assertions.assertEquals(expectedCodes, result, "Default incident code should be returned."); + } + + @Test + public void testSetItisCodesIncident_WithExistingProblemItisCode_ShouldReturnExistingItisCode() { + // Arrange + WydotTimIncident incident = new WydotTimIncident(); + incident.setProblem("268"); + List expectedItisCodes = List.of("268"); + + IncidentChoice mockIncidentChoice = new IncidentChoice(); + mockIncidentChoice.setCode("268"); + mockIncidentChoice.setItisCodeId(1); + mockIncidentChoice.setDescription("Speed Limit"); + when(mockIncidentChoicesService.selectAllIncidentProblems()).thenReturn(List.of(mockIncidentChoice)); + + ItisCode mockItisCode = new ItisCode(); + mockItisCode.setItisCode(268); + mockItisCode.setItisCodeId(1); + when(mockItisCodeService.selectAll()).thenReturn(List.of(mockItisCode)); + + // Act + List actualItisCodes = uut.setItisCodesIncident(incident); + + // Assert + Assertions.assertNotNull(actualItisCodes, "Resulting list should not be null."); + Assertions.assertEquals(expectedItisCodes, actualItisCodes, "The ITIS code for the incident problem should be returned."); + } + + @Test + public void testSetItisCodesIncident_OtherProblemGVW_ShouldReturnGVWItisCodes() { + // Arrange + WydotTimIncident incident = new WydotTimIncident(); + incident.setProblem("other"); + incident.setProblemOtherText("Weight limit of 60,000 GVW is in effect"); + List expectedItisCodes = List.of("2563", "2577", "11605", "8739"); + + // Act + List actualItisCodes = uut.setItisCodesIncident(incident); + + // Assert + Assertions.assertNotNull(actualItisCodes, "Resulting list should not be null."); + Assertions.assertEquals(expectedItisCodes, actualItisCodes, "The ITIS codes for a Gross Vehicle Weight restriction (60000 pounds) should be returned."); + } + + @Test + public void testSetItisCodesIncident_OtherProblemNoGVW_ShouldReturnDefaultIncidentCode() { + // Arrange + WydotTimIncident incident = new WydotTimIncident(); + incident.setProblem("other"); + incident.setProblemOtherText("Some other problem"); + List expectedItisCodes = List.of("531"); + + // Act + List actualItisCodes = uut.setItisCodesIncident(incident); + + // Assert + Assertions.assertNotNull(actualItisCodes, "Resulting list should not be null."); + Assertions.assertEquals(expectedItisCodes, actualItisCodes, "The default incident code should be returned."); + } + + @Test + public void testSetItisCodesIncident_OtherProblemNull_ShouldReturnDefaultIncidentCode() { + // Arrange + WydotTimIncident incident = new WydotTimIncident(); + incident.setProblem("other"); + incident.setProblemOtherText(null); + List expectedItisCodes = List.of("531"); + + // Act + List actualItisCodes = uut.setItisCodesIncident(incident); + + // Assert + Assertions.assertNotNull(actualItisCodes, "Resulting list should not be null."); + Assertions.assertEquals(expectedItisCodes, actualItisCodes, "The default incident code should be returned."); + } + + @Test + public void testSetItisCodesIncident_WeightNotFoundInProblemOtherText_ShouldReturnDefaultIncidentCode() { + // Arrange + WydotTimIncident incident = new WydotTimIncident(); + incident.setProblem("other"); + incident.setProblemOtherText("Some GVW weight restriction"); + List expectedItisCodes = List.of("531"); + + // Act + List actualItisCodes = uut.setItisCodesIncident(incident); + + // Assert + Assertions.assertNotNull(actualItisCodes, "Resulting list should not be null."); + Assertions.assertEquals(expectedItisCodes, actualItisCodes, "The default incident code should be returned."); + } + + @Test + public void testSetItisCodesIncident_WeightNotSupported_ShouldReturnDefaultIncidentCode() { + // Arrange + WydotTimIncident incident = new WydotTimIncident(); + incident.setProblem("other"); + incident.setProblemOtherText("Weight limit of 1,000,000 GVW is in effect"); + List expectedItisCodes = List.of("531"); + + // Act + List actualItisCodes = uut.setItisCodesIncident(incident); + + // Assert + Assertions.assertNotNull(actualItisCodes, "Resulting list should not be null."); + Assertions.assertEquals(expectedItisCodes, actualItisCodes, "The default incident code should be returned."); + } + } \ No newline at end of file diff --git a/pom.xml b/pom.xml index 95889eb51..d8100f086 100644 --- a/pom.xml +++ b/pom.xml @@ -10,9 +10,9 @@ - com.wyocv - wyo-cv - 1.4.0-SNAPSHOT + com.timm + tim-manager + 2.0.0 pom @@ -68,7 +68,7 @@ usdot.jpo.ode jpo-ode-core - 3.0.0 + 4.0.0 * @@ -79,7 +79,7 @@ usdot.jpo.ode jpo-ode-common - 3.0.0 + 4.0.0 * @@ -90,7 +90,7 @@ usdot.jpo.ode jpo-ode-plugins - 3.0.0 + 4.0.0 * @@ -101,7 +101,7 @@ usdot.jpo.ode jpo-ode-svcs - 3.0.0 + 4.0.0 * @@ -109,6 +109,12 @@ + + org.projectlombok + lombok + 1.18.30 + provided + @@ -118,6 +124,13 @@ 3.8.0 11 + + + org.projectlombok + lombok + 1.18.30 + + diff --git a/rsu-data-controller/Dockerfile b/rsu-data-controller/Dockerfile index bcc9a172d..b6dc43322 100644 --- a/rsu-data-controller/Dockerfile +++ b/rsu-data-controller/Dockerfile @@ -10,6 +10,6 @@ RUN apt-get install -y snmp RUN apt-get autoremove -y \ && apt-get clean -y -ADD rsu-data-controller-1.4.0-SNAPSHOT.jar /app/ +ADD rsu-data-controller-2.0.0.jar /app/ -CMD java -jar /app/rsu-data-controller-1.4.0-SNAPSHOT.jar \ No newline at end of file +CMD java -jar /app/rsu-data-controller-2.0.0.jar \ No newline at end of file diff --git a/rsu-data-controller/README.md b/rsu-data-controller/README.md index 4c0db6627..4e28ee512 100644 --- a/rsu-data-controller/README.md +++ b/rsu-data-controller/README.md @@ -23,7 +23,7 @@ These instructions will get you a copy of the project up and running on your loc - Maven is provided by the dev container's base image. ### Docker -The following instructions are intended to be executed from the root directory of the WyoCV project: +The following instructions are intended to be executed from the root directory of the TIMM project: 1. Reopen the project in the provided dev container by clicking on the blue button in the bottom left corner of the window and selecting "Reopen in Container". If Docker isn't running, start it and try again. 1. Open a terminal in the dev container by clicking on the `Terminal` menu and selecting `New Terminal` 1. Compile the project by running the following command: @@ -86,7 +86,7 @@ To run the application using the provided launch configuration, follow these ste 1. Click the green play button to start the application ## Deployment -This application is deployed using Docker, and is part of the larger WyoCVApplication suite. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. +This application is deployed using Docker, and is part of the larger TIM Manager. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. ## Configuration >SOME OF THESE PROPERTIES ARE SENSITIVE. DO NOT PUBLISH THEM TO VERSION CONTROL diff --git a/rsu-data-controller/pom.xml b/rsu-data-controller/pom.xml index 5fc377e44..86478d4d3 100644 --- a/rsu-data-controller/pom.xml +++ b/rsu-data-controller/pom.xml @@ -5,9 +5,9 @@ rsu-data-controller - com.wyocv - wyo-cv - 1.4.0-SNAPSHOT + com.timm + tim-manager + 2.0.0 diff --git a/sample.env b/sample.env index 936596b87..610c7b264 100644 --- a/sample.env +++ b/sample.env @@ -1,5 +1,5 @@ ################################### -# WyoCV Docker Environment File # +# TIMM Docker Environment File # ################################### # WARNING! The contents of this file may be sensitive. Take care not to add to source control. # @@ -99,7 +99,9 @@ REFRESH_CONFIG_ODE_URL=http://ode-url:8080 REFRESH_CONFIG_SDW_TTL=oneyear REFRESH_CONFIG_CV_REST_SERVICE=https://ode-url:8888 REFRESH_CONFIG_DEFAULT_LANE_WIDTH=50 -REFRESH_CRON_EXPRESSION=0 0 * * * ? +# Note: If the program runs in a GMT environment, "0 0 1 * * *" will be 1 AM GMT, which corresponds to 7 PM MDT. +# second minute hour day month day-of-week +REFRESH_CRON_EXPRESSION="0 0 1 * * *" REFRESH_CONFIG_ENV=dev REFRESH_CONFIG_RSU_ROUTES=route1, route2 REFRESH_CONFIG_POINT_INCIDENT_BUFFER_MILES=1 @@ -162,4 +164,8 @@ TASKS_CONFIG_RUN_HSM_CHECK=false TASKS_CONFIG_SDW_TTL=oneyear TASKS_CONFIG_DEFAULT_LANE_WIDTH=50 TASKS_CONFIG_RSU_ROUTES=route1, route2 -TASKS_CONFIG_POINT_INCIDENT_BUFFER_MILES=1 \ No newline at end of file +TASKS_CONFIG_POINT_INCIDENT_BUFFER_MILES=1 +TASKS_CONFIG_CLEANUP_STALE_ACTIVE_TIM_HOLDING_RECORDS_PERIOD_MINUTES=60 + +# log level for trihydro package +LOGGING_LEVEL_COM_TRIHYDRO=INFO \ No newline at end of file diff --git a/tim-refresh/Dockerfile b/tim-refresh/Dockerfile index 6467d799f..d6c4d5564 100644 --- a/tim-refresh/Dockerfile +++ b/tim-refresh/Dockerfile @@ -1,5 +1,5 @@ FROM maven:3.8-eclipse-temurin-21-alpine -ADD . /home/wyocv/wyocv_applications/tim-refresh +ADD . /home/timm/timm_applications/tim-refresh -CMD java -jar /home/wyocv/wyocv_applications/tim-refresh/tim-refresh-1.4.0-SNAPSHOT.jar +CMD java -jar /home/timm/timm_applications/tim-refresh/tim-refresh-2.0.0.jar diff --git a/tim-refresh/README.md b/tim-refresh/README.md index d8b763630..cafbce6c7 100644 --- a/tim-refresh/README.md +++ b/tim-refresh/README.md @@ -4,9 +4,9 @@ Traveler Information Messages (TIMs) have varying lifespans; some, like Variable Speed Limits (VSLs), can remain active for long periods. However, the signature that validates a TIM expires after two weeks. The `tim-refresh` module is designed to refresh TIMs that are still valid but have reached the two-week signature expiration limit. Each time the refresh task runs, the following steps are taken: -1. Active TIMs that are expiring within 24 hours are retrieved from the WyoCV database. The query excludes TIMs with a start time more than 24 hours in the future and TIMs with an end time less than 24 hours in the future. +1. Active TIMs that are expiring within 24 hours are retrieved from the TIMM database. The query excludes TIMs with a start time more than 24 hours in the future and TIMs with an end time less than 24 hours in the future. 1. The retrieved TIMs are validated. This check considers TIMs invalid if they are missing a start point, direction or route. -1. The expiration date for each TIM is reset to 'null' in the WyoCV database. +1. The expiration date for each TIM is reset to 'null' in the TIMM database. 1. Each TIM gets their TIM start time updated and is re-submitted to the ODE. 1. If any errors, invalid TIMs or exceptions are encountered, an email is sent to the configured alert addresses. @@ -23,7 +23,7 @@ By default, the refresh task runs once a day at 1 AM. These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See [deployment](#deployment) for notes on how to deploy the project on a live system. ### Docker -The following instructions are intended to be executed from the root directory of the WyoCV project: +The following instructions are intended to be executed from the root directory of the TIMM project: 1. Reopen the project in the provided dev container by clicking on the blue button in the bottom left corner of the window and selecting "Reopen in Container" 1. Open a terminal in the dev container by clicking on the `Terminal` menu and selecting `New Terminal` 1. Compile the project by running the following command: @@ -81,12 +81,12 @@ To run the application using the provided launch configuration, follow these ste 1. Open the Run and Debug sidebar by clicking on the icon on the left side of the window or by pressing `Ctrl+Shift+D` 1. Click on the gear icon in the top right corner of the sidebar 1. Select the `TIM Refresh (Launch)` configuration from the dropdown menu -1. If running integration tests, verify that the CV Data Controller and the WyoCV database are running and accessible at the addresses specified in the .env file +1. If running integration tests, verify that the CV Data Controller and the TIMM database are running and accessible at the addresses specified in the .env file 1. If running integration tests, verify that the ODE is running and accessible at the address specified in the .env file 1. Click the green play button to start the application ## Deployment -This application is deployed using Docker, and is part of the larger WyoCVApplication suite. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. +This application is deployed using Docker, and is part of the larger TIM Manager. The associated Dockerfile is configured for the development ODE environment. See the main [README](../README.md) for the project and associated [docker-compose](../docker-compose.yml), and [sample.env](../sample.env) file for further deployment configurations. ## Configuration **SOME OF THESE PROPERTIES ARE SENSITIVE. DO NOT PUBLISH THEM TO VERSION CONTROL** diff --git a/tim-refresh/pom.xml b/tim-refresh/pom.xml index b404c34ff..d0525da3b 100644 --- a/tim-refresh/pom.xml +++ b/tim-refresh/pom.xml @@ -4,9 +4,9 @@ tim-refresh - com.wyocv - wyo-cv - 1.4.0-SNAPSHOT + com.timm + tim-manager + 2.0.0 diff --git a/tim-refresh/src/main/java/com/trihydro/timrefresh/TimRefreshController.java b/tim-refresh/src/main/java/com/trihydro/timrefresh/TimRefreshController.java index 60d1522fe..bb4109499 100644 --- a/tim-refresh/src/main/java/com/trihydro/timrefresh/TimRefreshController.java +++ b/tim-refresh/src/main/java/com/trihydro/timrefresh/TimRefreshController.java @@ -1,5 +1,6 @@ package com.trihydro.timrefresh; +import com.trihydro.library.model.ActiveTim; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -9,45 +10,49 @@ import com.google.gson.Gson; import com.trihydro.library.helpers.EmailHelper; import com.trihydro.library.helpers.TimGenerationHelper; -import com.trihydro.library.helpers.Utility; import com.trihydro.library.model.Logging_TimUpdateModel; import com.trihydro.library.model.ResubmitTimException; import com.trihydro.library.model.TimUpdateModel; import com.trihydro.library.service.ActiveTimService; import com.trihydro.timrefresh.config.TimRefreshConfiguration; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component +@Slf4j public class TimRefreshController { private final SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); public Gson gson = new Gson(); protected TimRefreshConfiguration configuration; - private Utility utility; - private EmailHelper emailHelper; - private ActiveTimService activeTimService; - private TimGenerationHelper timGenerationHelper; + private final EmailHelper emailHelper; + private final ActiveTimService activeTimService; + private final TimGenerationHelper timGenerationHelper; @Autowired - public TimRefreshController(TimRefreshConfiguration configurationRhs, Utility _utility, - ActiveTimService _activeTimService, EmailHelper _emailHelper, TimGenerationHelper _timGenerationHelper) { - configuration = configurationRhs; - utility = _utility; - activeTimService = _activeTimService; - emailHelper = _emailHelper; - timGenerationHelper = _timGenerationHelper; + public TimRefreshController(TimRefreshConfiguration configurationRhs, ActiveTimService activeTimService, EmailHelper emailHelper, + TimGenerationHelper timGenerationHelper) { + this.configuration = configurationRhs; + this.activeTimService = activeTimService; + this.emailHelper = emailHelper; + this.timGenerationHelper = timGenerationHelper; } - @Scheduled(cron = "${cron.expression}") // run at 1:00am every day + /** + * This method is intended to be run once a day, but can be configured to run at + * any time. It will check for Active TIMs that are expiring within 24 hours and + * issue new TIMs to the ODE for those that are expiring. + */ + @Scheduled(cron = "${cron.expression}") public void performTaskUsingCron() { - utility.logWithDate("Regular task performed using Cron at " + dateFormat.format(new Date())); + log.info("Regular task performed using Cron at {}", dateFormat.format(new Date())); // fetch Active_TIM that are expiring within 24 hrs List expiringTims = activeTimService.getExpiringActiveTims(); - utility.logWithDate(expiringTims.size() + " expiring TIMs found"); + log.info("{} expiring TIMs found", expiringTims.size()); List invalidTims = new ArrayList(); List exceptionTims = new ArrayList<>(); List timsToRefresh = new ArrayList(); @@ -68,48 +73,50 @@ public void performTaskUsingCron() { var resetSuccessful = true; // attempt to refresh TIMs and collect any exceptions - if (timsToRefresh.size() > 0) { - var activeTimIds = timsToRefresh.stream().map(x -> x.getActiveTimId()).collect(Collectors.toList()); + if (!timsToRefresh.isEmpty()) { + var activeTimIds = timsToRefresh.stream().map(ActiveTim::getActiveTimId).collect(Collectors.toList()); // Reset expiration dates so they'll be updated after messages are processed. // Success isn't critical to proceed. We'll just end up with redundant resubmissions later on. resetSuccessful = activeTimService.resetActiveTimsExpirationDate(activeTimIds); exceptionTims = timGenerationHelper.resetTimStartTimeAndResubmitToOde(activeTimIds); } - if (invalidTims.size() > 0 || exceptionTims.size() > 0 || !resetSuccessful) { + if (!invalidTims.isEmpty() || !exceptionTims.isEmpty() || !resetSuccessful) { String body = ""; - if(!resetSuccessful) { + if (!resetSuccessful) { body += "An error occurred while resetting the expiration date(s) for the Active TIM(s)"; body += "

"; } - if (invalidTims.size() > 0) { + if (!invalidTims.isEmpty()) { body += "The Tim Refresh application found invalid TIM(s) while attempting to refresh."; body += "
"; body += "The associated ActiveTim records are:
"; + StringBuilder bodyBuilder = new StringBuilder(body); for (Logging_TimUpdateModel timUpdateModel : invalidTims) { - body += gson.toJson(timUpdateModel); - body += "

"; + bodyBuilder.append(gson.toJson(timUpdateModel)); + bodyBuilder.append("

"); } + body = bodyBuilder.toString(); } - if (exceptionTims.size() > 0) { + if (!exceptionTims.isEmpty()) { body += "The TIM Refresh application ran into exceptions while attempting to resubmit TIMs. The following exceptions were found: "; body += "
"; + StringBuilder bodyBuilder = new StringBuilder(body); for (ResubmitTimException rte : exceptionTims) { - body += gson.toJson(rte); - body += "
"; + bodyBuilder.append(gson.toJson(rte)); + bodyBuilder.append("
"); } + body = bodyBuilder.toString(); } try { - utility.logWithDate( - "Sending error email. The following TIM exceptions were found: " + gson.toJson(body)); + log.info("Sending error email. The following TIM exceptions were found: {}", gson.toJson(body)); emailHelper.SendEmail(configuration.getAlertAddresses(), "TIM Refresh Exceptions", body); } catch (Exception e) { - utility.logWithDate("Exception attempting to send email for invalid TIM:"); - e.printStackTrace(); + log.error("Exception attempting to send email for invalid TIM", e); } } } diff --git a/tim-refresh/src/main/java/com/trihydro/timrefresh/config/TimRefreshConfiguration.java b/tim-refresh/src/main/java/com/trihydro/timrefresh/config/TimRefreshConfiguration.java index ca32f52d2..51aecd98e 100644 --- a/tim-refresh/src/main/java/com/trihydro/timrefresh/config/TimRefreshConfiguration.java +++ b/tim-refresh/src/main/java/com/trihydro/timrefresh/config/TimRefreshConfiguration.java @@ -1,5 +1,6 @@ package com.trihydro.timrefresh.config; +import com.trihydro.library.exceptionhandlers.IdenticalPointsExceptionHandler; import java.math.BigDecimal; import com.trihydro.library.helpers.EmailHelper; @@ -39,7 +40,7 @@ OdeService.class, PathNodeXYService.class, RegionService.class, RsuService.class, SdwService.class, Utility.class, RestTemplateProvider.class, MilepostReduction.class, JavaMailSenderImplProvider.class, EmailHelper.class, TimGenerationHelper.class, PathNodeLLService.class, SnmpHelper.class, - RegionNameTrimmer.class, CreateBaseTimUtil.class }) + RegionNameTrimmer.class, CreateBaseTimUtil.class, IdenticalPointsExceptionHandler.class }) public class TimRefreshConfiguration implements CVRestServiceProps, SdwProps, EmailProps, OdeProps, TimGenerationProps { private TimeToLive sdwTtl; diff --git a/tim-refresh/src/main/resources/application-dev.properties b/tim-refresh/src/main/resources/application-dev.properties index 7cf463dd2..29beca759 100644 --- a/tim-refresh/src/main/resources/application-dev.properties +++ b/tim-refresh/src/main/resources/application-dev.properties @@ -13,4 +13,5 @@ config.environmentName=DEV config.mailHost=localhost config.mailPort=25 -cron.expression=0 0 * * * * \ No newline at end of file +# second minute hour day month day-of-week +cron.expression=0 0 1 * * * \ No newline at end of file