Compare commits

...

2 Commits

44 changed files with 10991 additions and 22 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
/mvnw text eol=lf
*.cmd text eol=crlf

51
.gitignore vendored
View File

@ -1,26 +1,33 @@
# ---> Java
# Compiled class file
*.class
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
# Log file
*.log
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
# BlueJ files
*.ctxt
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/

19
.mvn/wrapper/maven-wrapper.properties vendored Normal file
View File

@ -0,0 +1,19 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
wrapperVersion=3.3.2
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip

259
mvnw vendored Normal file
View File

@ -0,0 +1,259 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.3.2
#
# Optional ENV vars
# -----------------
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
# MVNW_REPOURL - repo url base for downloading maven distribution
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
# ----------------------------------------------------------------------------
set -euf
[ "${MVNW_VERBOSE-}" != debug ] || set -x
# OS specific support.
native_path() { printf %s\\n "$1"; }
case "$(uname)" in
CYGWIN* | MINGW*)
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
native_path() { cygpath --path --windows "$1"; }
;;
esac
# set JAVACMD and JAVACCMD
set_java_home() {
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
if [ -n "${JAVA_HOME-}" ]; then
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACCMD="$JAVA_HOME/jre/sh/javac"
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACCMD="$JAVA_HOME/bin/javac"
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
return 1
fi
fi
else
JAVACMD="$(
'set' +e
'unset' -f command 2>/dev/null
'command' -v java
)" || :
JAVACCMD="$(
'set' +e
'unset' -f command 2>/dev/null
'command' -v javac
)" || :
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
return 1
fi
fi
}
# hash string like Java String::hashCode
hash_string() {
str="${1:-}" h=0
while [ -n "$str" ]; do
char="${str%"${str#?}"}"
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
str="${str#?}"
done
printf %x\\n $h
}
verbose() { :; }
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
die() {
printf %s\\n "$1" >&2
exit 1
}
trim() {
# MWRAPPER-139:
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
# Needed for removing poorly interpreted newline sequences when running in more
# exotic environments such as mingw bash on Windows.
printf "%s" "${1}" | tr -d '[:space:]'
}
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
while IFS="=" read -r key value; do
case "${key-}" in
distributionUrl) distributionUrl=$(trim "${value-}") ;;
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
esac
done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
case "${distributionUrl##*/}" in
maven-mvnd-*bin.*)
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
*)
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
distributionPlatform=linux-amd64
;;
esac
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
;;
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
esac
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
distributionUrlName="${distributionUrl##*/}"
distributionUrlNameMain="${distributionUrlName%.*}"
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
exec_maven() {
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
}
if [ -d "$MAVEN_HOME" ]; then
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
exec_maven "$@"
fi
case "${distributionUrl-}" in
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
esac
# prepare tmp dir
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
trap clean HUP INT TERM EXIT
else
die "cannot create temp dir"
fi
mkdir -p -- "${MAVEN_HOME%/*}"
# Download and Install Apache Maven
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
verbose "Downloading from: $distributionUrl"
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
# select .zip or .tar.gz
if ! command -v unzip >/dev/null; then
distributionUrl="${distributionUrl%.zip}.tar.gz"
distributionUrlName="${distributionUrl##*/}"
fi
# verbose opt
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
# normalize http auth
case "${MVNW_PASSWORD:+has-password}" in
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
esac
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
verbose "Found wget ... using wget"
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
verbose "Found curl ... using curl"
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
elif set_java_home; then
verbose "Falling back to use Java to download"
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
cat >"$javaSource" <<-END
public class Downloader extends java.net.Authenticator
{
protected java.net.PasswordAuthentication getPasswordAuthentication()
{
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
}
public static void main( String[] args ) throws Exception
{
setDefault( new Downloader() );
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
}
}
END
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
verbose " - Compiling Downloader.java ..."
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
verbose " - Running Downloader.java ..."
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
fi
# If specified, validate the SHA-256 sum of the Maven distribution zip file
if [ -n "${distributionSha256Sum-}" ]; then
distributionSha256Result=false
if [ "$MVN_CMD" = mvnd.sh ]; then
echo "Checksum validation is not supported for maven-mvnd." >&2
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
exit 1
elif command -v sha256sum >/dev/null; then
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
distributionSha256Result=true
fi
elif command -v shasum >/dev/null; then
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
distributionSha256Result=true
fi
else
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
exit 1
fi
if [ $distributionSha256Result = false ]; then
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
exit 1
fi
fi
# unzip and move
if command -v unzip >/dev/null; then
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
else
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
fi
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
clean || :
exec_maven "$@"

149
mvnw.cmd vendored Normal file
View File

@ -0,0 +1,149 @@
<# : batch portion
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Apache Maven Wrapper startup batch script, version 3.3.2
@REM
@REM Optional ENV vars
@REM MVNW_REPOURL - repo url base for downloading maven distribution
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
@REM ----------------------------------------------------------------------------
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
@SET __MVNW_CMD__=
@SET __MVNW_ERROR__=
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
@SET PSModulePath=
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
)
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
@SET __MVNW_PSMODULEP_SAVE=
@SET __MVNW_ARG0_NAME__=
@SET MVNW_USERNAME=
@SET MVNW_PASSWORD=
@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
@echo Cannot start maven from wrapper >&2 && exit /b 1
@GOTO :EOF
: end batch / begin powershell #>
$ErrorActionPreference = "Stop"
if ($env:MVNW_VERBOSE -eq "true") {
$VerbosePreference = "Continue"
}
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
if (!$distributionUrl) {
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
}
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
"maven-mvnd-*" {
$USE_MVND = $true
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
$MVN_CMD = "mvnd.cmd"
break
}
default {
$USE_MVND = $false
$MVN_CMD = $script -replace '^mvnw','mvn'
break
}
}
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
if ($env:MVNW_REPOURL) {
$MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
}
$distributionUrlName = $distributionUrl -replace '^.*/',''
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
if ($env:MAVEN_USER_HOME) {
$MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
}
$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
exit $?
}
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
}
# prepare tmp dir
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
trap {
if ($TMP_DOWNLOAD_DIR.Exists) {
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
}
}
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
# Download and Install Apache Maven
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
Write-Verbose "Downloading from: $distributionUrl"
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
$webclient = New-Object System.Net.WebClient
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
# If specified, validate the SHA-256 sum of the Maven distribution zip file
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
if ($distributionSha256Sum) {
if ($USE_MVND) {
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
}
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
}
}
# unzip and move
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
try {
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
} catch {
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
Write-Error "fail to move MAVEN_HOME"
}
} finally {
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
}
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"

101
pom.xml Normal file
View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dongni</groupId>
<artifactId>CollisionAvoidance</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>CollisionAvoidance</name>
<description>CollisionAvoidance</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>3.0.12</version>
</dependency>
<!-- Spring Kafka -->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- MongoDB依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- Jackson依赖用于Redis序列化 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,16 @@
package com.dongni.collisionavoidance;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
@EnableScheduling
@SpringBootApplication
@EnableMongoRepositories
public class CollisionAvoidanceApplication {
public static void main(String[] args) {
SpringApplication.run(CollisionAvoidanceApplication.class, args);
}
}

View File

@ -0,0 +1,24 @@
package com.dongni.collisionavoidance.common.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
/**
* 自定义线程池避免定时任务单线程阻塞的情况
*/
@Configuration
@EnableAsync // 启用异步支持
public class SchedulerConfig {
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
// 设置线程池大小根据需求调整
scheduler.setPoolSize(3);
// 设置线程名称前缀
scheduler.setThreadNamePrefix("ScheduledTask-");
return scheduler;
}
}

View File

@ -0,0 +1,42 @@
package com.dongni.collisionavoidance.common.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 航空器实体类
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Aircraft extends MovingObject{
/**
* 航班号
*/
@NotBlank(message = "航班号不能为空")
private String flightNo;
/**
* 航迹号
*/
private Long trackNumber;
@JsonCreator
public Aircraft(
@JsonProperty("latitude") double latitude,
@JsonProperty("longitude") double longitude,
@JsonProperty("altitude") double altitude,
@JsonProperty("time") long timestamp
) {
this.currentPosition = new GeoPosition(latitude, longitude, altitude);
this.timestamp = timestamp;
}
}

View File

@ -0,0 +1,22 @@
package com.dongni.collisionavoidance.common.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor // 必须有无参构造函数
@AllArgsConstructor
public class GeoPosition {
// 纬度
@JsonProperty("latitude")
public double latitude;
// 经度
@JsonProperty("longitude")
public double longitude;
// 高度
@JsonProperty("altitude")
public double altitude;
}

View File

@ -0,0 +1,19 @@
package com.dongni.collisionavoidance.common.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MovementState {
public GeoPosition position;
//速度
public Velocity velocity;
// 航向
public double heading;
// 时间戳毫秒
public long timestamp;
}

View File

@ -0,0 +1,37 @@
package com.dongni.collisionavoidance.common.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayDeque;
import java.util.Deque;
@Data
@NoArgsConstructor
@AllArgsConstructor
public abstract class MovingObject {
// 当前坐标
public GeoPosition currentPosition;
//速度
public Velocity velocity;
// 航向
public double heading;
// 时间戳毫秒
public long timestamp;
// 历史状态队列
public Deque<MovementState> stateHistory = new ArrayDeque<>();
// 最大历史记录数
public int MAX_HISTORY = 10;
// 最大速度子类初始化
public double maxSpeed;
// 类型枚举
public MovingObjectType type;
}

View File

@ -0,0 +1,10 @@
package com.dongni.collisionavoidance.common.model;
public enum MovingObjectType {
// 航空器飞机
AIRCRAFT,
// 特勤车辆不可控
SPECIAL_VEHICLE,
// 无人车可控
UNMANNED_VEHICLE
}

View File

@ -0,0 +1,15 @@
package com.dongni.collisionavoidance.common.model;
public class PositionRecord {
private final GeoPosition position;
private final long timestamp; // 时间戳毫秒
public PositionRecord(GeoPosition position, long timestamp) {
this.position = position;
this.timestamp = timestamp;
}
// Getters
public GeoPosition getPosition() { return position; }
public long getTimestamp() { return timestamp; }
}

View File

@ -0,0 +1,43 @@
package com.dongni.collisionavoidance.common.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Deque;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SpecialVehicle extends MovingObject{
/**
* 车牌号
*/
@NotBlank(message = "车牌号不能为空")
@JsonProperty("vehicleNo")
private String vehicleNo;
public SpecialVehicle(
@JsonProperty("latitude") double latitude,
@JsonProperty("longitude") double longitude,
@JsonProperty("time") long timestamp,
@JsonProperty("speed") double speed,
@JsonProperty("direction") double heading
) {
this.currentPosition = new GeoPosition(latitude, longitude, 0);
double radians = Math.toRadians(heading);
double vx = speed *Math.sin(radians);
double vy = speed *Math.cos(radians);
// 对微小值归零阈值可根据需求调整
vx = Math.abs(vx) < 1e-10 ? 0.0 : vx;
vy = Math.abs(vy) < 1e-10 ? 0.0 : vy;
this.velocity = new Velocity(vx,vy,0);
this.heading = heading;
this.timestamp = timestamp;
}
}

View File

@ -0,0 +1,45 @@
package com.dongni.collisionavoidance.common.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Deque;
/**
* 车辆实体类
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UnmannedVehicle extends MovingObject{
/**
* 消息唯一ID消息的唯一标识符
*/
private String transId;
/**
* 车牌号
*/
@NotBlank(message = "车牌号不能为空")
private String vehicleId;
public UnmannedVehicle(
@JsonProperty("longitude") double longitude,
@JsonProperty("latitude") double latitude,
@JsonProperty("direction") double heading,
@JsonProperty("speed") double speed
) {
this.currentPosition = new GeoPosition(longitude, latitude, 0);
double vx = speed *Math.sin(heading);
double vy = speed *Math.cos(heading);
// 对微小值归零阈值可根据需求调整
vx = Math.abs(vx) < 1e-10 ? 0.0 : vx;
vy = Math.abs(vy) < 1e-10 ? 0.0 : vy;
this.velocity = new Velocity(vx,vy,0);
this.heading = heading;
}
}

View File

@ -0,0 +1,21 @@
package com.dongni.collisionavoidance.common.model;
import lombok.Data;
@Data
public class Velocity {
// 东向速度/
private final double x;
// 北向速度/
private final double y;
// 垂直速度/
private final double z;
// 计算速度标量
public double getSpeed() {
return Math.sqrt(x * x + y * y + z * z);
}
}

View File

@ -0,0 +1,8 @@
package com.dongni.collisionavoidance.common.model.base;
public class Constant {
public static final String VEHICLE_LOCATION_KEY_PREFIX = "vehicle:location:";
// 60秒后过期
public static final long LOCATION_EXPIRE_TIME = 60;
}

View File

@ -0,0 +1,71 @@
package com.dongni.collisionavoidance.common.model.base;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 通用响应对象
* @param <T> 响应数据类型
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Response<T> {
/**
* 状态码
*/
private Integer status;
/**
* 响应消息
*/
private String message;
/**
* 响应数据
*/
private T data;
/**
* 创建成功响应
*/
public static <T> Response<T> success(T data) {
return Response.<T>builder()
.status(200)
.message("操作成功")
.data(data)
.build();
}
/**
* 创建成功响应带自定义消息
*/
public static <T> Response<T> success(String message, T data) {
return Response.<T>builder()
.status(200)
.message(message)
.data(data)
.build();
}
/**
* 创建错误响应
*/
public static <T> Response<T> error(Integer status, String message) {
return Response.<T>builder()
.status(status)
.message(message)
.build();
}
/**
* 创建错误响应默认500错误
*/
public static <T> Response<T> error(String message) {
return error(500, message);
}
}

View File

@ -0,0 +1,18 @@
package com.dongni.collisionavoidance.common.model.dto;
import lombok.Data;
// 对应原始数据结构
@Data
public class AircraftDTO {
private String flightNo;
private Long trackNumber;
private double latitude;
private double longitude;
private double altitude;
private long time;
// 必须包含默认构造器
public AircraftDTO() {}
}

View File

@ -0,0 +1,31 @@
package com.dongni.collisionavoidance.common.model.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SpecialVehicleDTO {
@NotBlank(message = "车牌号不能为空")
private String vehicleNo;
@NotNull(message = "经度不能为空")
private Double longitude;
@NotNull(message = "纬度不能为空")
private Double latitude;
@NotNull(message = "时间戳不能为空")
private Long time;
private Double direction;
private Double speed;
}

View File

@ -0,0 +1,39 @@
package com.dongni.collisionavoidance.config;
import com.dongni.collisionavoidance.dataCollector.model.VehicleLocationInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, VehicleLocationInfo> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, VehicleLocationInfo> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer<VehicleLocationInfo> serializer =
new Jackson2JsonRedisSerializer<>(VehicleLocationInfo.class);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
template.setHashValueSerializer(serializer);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
}

View File

@ -0,0 +1,34 @@
package com.dongni.collisionavoidance.dataCollector.config;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
// @Bean
// public RestTemplate restTemplate() {
// return new RestTemplate();
// }
@Bean
public RestTemplate restTemplate(ObjectMapper objectMapper) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().forEach(converter -> {
if (converter instanceof MappingJackson2HttpMessageConverter) {
((MappingJackson2HttpMessageConverter) converter).setObjectMapper(objectMapper);
}
});
return restTemplate;
}
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper;
}
}

View File

@ -0,0 +1,143 @@
package com.dongni.collisionavoidance.dataCollector.dao;
import com.dongni.collisionavoidance.common.model.Aircraft;
import com.dongni.collisionavoidance.common.model.SpecialVehicle;
import com.dongni.collisionavoidance.common.model.UnmannedVehicle;
import com.dongni.collisionavoidance.common.model.base.Response;
import com.dongni.collisionavoidance.common.model.dto.AircraftDTO;
import com.dongni.collisionavoidance.common.model.dto.SpecialVehicleDTO;
import com.dongni.collisionavoidance.dataCollector.document.VehicleLocationDocument;
import com.dongni.collisionavoidance.dataCollector.model.VehicleLocationInfo;
import com.dongni.collisionavoidance.dataCollector.service.AuthService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
@Slf4j
@Component
public class DataCollectorDao {
// 无人车厂商数据源相关配置
@Value("${data.collector.vehicle-api.base-url}")
private String vehicleBaseUrl;
@Value("${data.collector.vehicle-api.endpoints.vehicle-location}")
private String vehicleLocationEndpoint;
private final RestTemplate restTemplate;
private final AuthService authService;
public DataCollectorDao(RestTemplate restTemplate, AuthService authService) {
this.restTemplate = restTemplate;
this.authService = authService;
}
public List<Aircraft> collectAircraftData(String endpoint, String baseUrl) {
try {
String url = UriComponentsBuilder
.fromHttpUrl(baseUrl)
.path(endpoint)
.toUriString();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", authService.getToken());
HttpEntity<Void> requestEntity = new HttpEntity<>(headers);
ResponseEntity<Response<List<Aircraft>>> response = restTemplate.exchange(
url,
HttpMethod.GET,
requestEntity,
new ParameterizedTypeReference<Response<List<Aircraft>>>() {}
);
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
List<Aircraft> dataList = response.getBody().getData();
log.info("成功获取航空器数据,数量: {}", dataList.size());
return dataList;
}
} catch (Exception e) {
log.error("采集航空器数据失败: {}", endpoint, e);
}
return Collections.emptyList();
}
public List<SpecialVehicle> collectVehicleData(String endpoint, String baseUrl) {
try {
String url = UriComponentsBuilder
.fromHttpUrl(baseUrl)
.path(endpoint)
.toUriString();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", authService.getToken());
HttpEntity<Void> requestEntity = new HttpEntity<>(headers);
ResponseEntity<Response<List<SpecialVehicle>>> response = restTemplate.exchange(
url,
HttpMethod.GET,
requestEntity,
new ParameterizedTypeReference<Response<List<SpecialVehicle>>>() {}
);
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
List<SpecialVehicle> dataList = response.getBody().getData();
log.info("成功获取特种车辆数据,数量: {}", dataList.size());
return dataList;
}
} catch (Exception e) {
log.error("采集特种车辆数据失败: {}", endpoint, e);
}
return Collections.emptyList();
}
/**
* 获取车辆定位信息
* @return 车辆定位信息列表
*/
public List<UnmannedVehicle> getVehicleLocationInfo() {
System.out.println("接口被调用");
try {
String url = UriComponentsBuilder
.fromHttpUrl(vehicleBaseUrl)
.path(vehicleLocationEndpoint)
.toUriString();
HttpHeaders headers = new HttpHeaders();
HttpEntity<Void> requestEntity = new HttpEntity<>(headers);
ResponseEntity<List<UnmannedVehicle>> response = restTemplate.exchange(
url,
HttpMethod.GET,
requestEntity,
new ParameterizedTypeReference<List<UnmannedVehicle>>() {}
);
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
log.info("成功获取车辆定位信息,数量: {}", response.getBody().size());
return response.getBody();
} else {
log.error("获取车辆定位信息失败,状态码: {}", response.getStatusCode());
return Collections.emptyList();
}
} catch (Exception e) {
log.error("获取车辆定位信息时发生异常", e);
return Collections.emptyList();
}
}
}

View File

@ -0,0 +1,24 @@
package com.dongni.collisionavoidance.dataCollector.document;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import java.time.LocalDateTime;
@Document(collection = "vehicle_locations")
@Data
public class VehicleLocationDocument {
@Id
private String id;
private String vehicleId;
private double longitude;
private double latitude;
private double direction;
private double speed;
private long timestamp;
@Indexed
private LocalDateTime createTime;
}

View File

@ -0,0 +1,11 @@
package com.dongni.collisionavoidance.dataCollector.model;
import lombok.Data;
@Data
public class CommandResponse {
private int code; // 状态码
private String msg; // 消息
private String transId; // 消息唯一id
private long timestamp; // 时间戳
}

View File

@ -0,0 +1,23 @@
package com.dongni.collisionavoidance.dataCollector.model;
import com.dongni.collisionavoidance.dataCollector.model.enums.CommandType;
import com.dongni.collisionavoidance.dataCollector.model.enums.CommandReason;
import com.dongni.collisionavoidance.dataCollector.model.enums.SignalState;
import lombok.Data;
@Data
public class VehicleCommand {
private String transId; // 消息唯一id
private long timestamp; // 时间戳
private String vehicleId; // 车辆ID
private CommandType commandType; // 指令类型
private CommandReason commandReason; // 指令原因
private SignalState signalState; // 信号灯状态
private String intersectionId; // 路口ID
private double latitude; // 目标位置纬度
private double longitude; // 目标位置经度
private double relativeSpeed; // 相对速度
private double relativeMotionX; // 相对运动X分量
private double relativeMotionY; // 相对运动Y分量
private double minDistance; // 最小距离
}

View File

@ -0,0 +1,14 @@
package com.dongni.collisionavoidance.dataCollector.model;
import lombok.Data;
@Data
public class VehicleLocationInfo {
private String transId; // 消息唯一id
private long timestamp; // 时间戳
private String vehicleId; // 车辆ID
private double longitude; // 经度
private double latitude; // 纬度
private double direction; // 车头航向角
private double speed; // 车速
}

View File

@ -0,0 +1,24 @@
package com.dongni.collisionavoidance.dataCollector.model;
import lombok.Data;
import java.util.List;
@Data
public class VehicleStateInfo {
private String transId;
private long timestamp;
private String vehicleId;
private boolean loginStatus;
private List<String> faultInfo;
private boolean activeSafety;
private boolean rc;
private int command;
private List<String> airportInfo;
private int vehicleMode;
private int gearState;
private boolean chassisReady;
private boolean collisionStatus;
private int clearance;
private int turnSignalStatus;
private List<Byte> pointCloud;
}

View File

@ -0,0 +1,10 @@
package com.dongni.collisionavoidance.dataCollector.model.enums;
public enum CommandReason {
TRAFFIC_LIGHT, // 红绿灯控制
AIRCRAFT_CROSSING, // 航空器交叉
SPECIAL_VEHICLE, // 特勤车辆
AIRCRAFT_PUSH, // 航空器推出
RESUME_TRAFFIC, // 恢复通行
PARKING_SIDE // 安全停靠
}

View File

@ -0,0 +1,9 @@
package com.dongni.collisionavoidance.dataCollector.model.enums;
public enum CommandType {
ALERT, // 告警指令
SIGNAL, // 信号灯指令
WARNING, // 预警指令
RESUME, // 恢复指令
PARKING // 安全停靠
}

View File

@ -0,0 +1,7 @@
package com.dongni.collisionavoidance.dataCollector.model.enums;
public enum SignalState {
RED, // 红灯
YELLOW, // 黄灯
GREEN // 绿灯
}

View File

@ -0,0 +1,13 @@
package com.dongni.collisionavoidance.dataCollector.repository;
import com.dongni.collisionavoidance.dataCollector.document.VehicleLocationDocument;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface VehicleLocationRepository extends MongoRepository<VehicleLocationDocument, String> {
List<VehicleLocationDocument> findByVehicleIdAndTimestampBetween(
String vehicleId, long startTime, long endTime);
}

View File

@ -0,0 +1,129 @@
package com.dongni.collisionavoidance.dataCollector.service;
import com.dongni.collisionavoidance.common.model.base.Response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
@Slf4j
@Service
public class AuthService {
@Value("${data.collector.airport-api.auth.username}")
private String username;
@Value("${data.collector.airport-api.auth.password}")
private String password;
@Value("${data.collector.airport-api.base-url}")
private String baseUrl;
@Value("${data.collector.airport-api.endpoints.login}")
private String loginEndpoint;
@Value("${data.collector.airport-api.endpoints.refresh}")
private String refreshEndpoint;
private final RestTemplate restTemplate;
private String token;
private long tokenExpiryTime;
public AuthService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
//登录获取Token
public String loginAndGetToken() {
String loginUrl = UriComponentsBuilder
.fromHttpUrl(baseUrl)
.path(loginEndpoint)
.queryParam("username", username)
.queryParam("password", password)
.toUriString();
try {
ResponseEntity<Response<String>> response = restTemplate.exchange(
loginUrl,
HttpMethod.POST,
null,
new ParameterizedTypeReference<>() {
}
);
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
this.token = response.getBody().getData();
this.tokenExpiryTime = System.currentTimeMillis() + 3600 * 1000;
log.info("Successfully obtained new token");
return this.token;
}
} catch (Exception e) {
log.error("Failed to login: ", e);
}
throw new RuntimeException("Failed to obtain token");
}
//Token续时
public String refreshToken() {
String refreshUrl = UriComponentsBuilder
.fromHttpUrl(baseUrl)
.path(refreshEndpoint)
.toUriString();
try {
// 创建带有当前token的请求头
HttpEntity<?> requestEntity = new HttpEntity<>(createAuthHeader());
ResponseEntity<Response<String>> response = restTemplate.exchange(
refreshUrl,
HttpMethod.GET,
requestEntity,
new ParameterizedTypeReference<Response<String>>() {}
);
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
this.token = response.getBody().getData();
this.tokenExpiryTime = System.currentTimeMillis() + 3600 * 1000;
log.info("Successfully refreshed token");
return this.token;
}
} catch (Exception e) {
log.error("Failed to refresh token: ", e);
}
// 如果续期失败尝试重新登录
return loginAndGetToken();
}
//创造带有Token的请求头
private HttpHeaders createAuthHeader() {
HttpHeaders headers = new HttpHeaders();
if (token != null) {
headers.set("Authorization", token);
}
return headers;
}
//获取Token
public String getToken() {
long currentTime = System.currentTimeMillis();
if (token == null) {
return loginAndGetToken();
}
// 如果token已过期重新登录
if (currentTime >= tokenExpiryTime) {
return loginAndGetToken();
}
// 如果token即将过期比如还有10分钟过期尝试续期
if (currentTime >= tokenExpiryTime - 600_000) {
return refreshToken();
}
return token;
}
}

View File

@ -0,0 +1,26 @@
package com.dongni.collisionavoidance.dataCollector.service;
import com.dongni.collisionavoidance.dataCollector.document.VehicleLocationDocument;
import com.mongodb.client.result.DeleteResult;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import java.time.LocalDateTime;
@Service
@Slf4j
public class DataCleanupService {
// private final MongoTemplate mongoTemplate;
//
// @Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点执行
// public void cleanupOldData() {
// LocalDateTime threshold = LocalDateTime.now().minusDays(30);
// Query query = Query.query(Criteria.where("createTime").lt(threshold));
// DeleteResult result = mongoTemplate.remove(query, VehicleLocationDocument.class);
// log.info("Cleaned up {} old location records", result.getDeletedCount());
// }
}

View File

@ -0,0 +1,208 @@
package com.dongni.collisionavoidance.dataCollector.service;
import com.dongni.collisionavoidance.common.model.*;
import com.dongni.collisionavoidance.common.model.dto.AircraftDTO;
import com.dongni.collisionavoidance.common.model.dto.SpecialVehicleDTO;
import com.dongni.collisionavoidance.dataCollector.dao.DataCollectorDao;
import com.dongni.collisionavoidance.dataCollector.model.VehicleLocationInfo;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.*;
import java.util.ArrayList;
import java.util.LinkedList;
@Slf4j
@Service
public class DataCollectorService {
// 机场数据源相关配置
@Value("${data.collector.airport-api.endpoints.vehicle}")
private String airportVehicleEndpoint;
@Value("${data.collector.airport-api.endpoints.aircraft}")
private String airportAircraftEndpoint;
@Value("${data.collector.airport-api.base-url}")
private String airportBaseUrl;
// 线程安全队列用于暂存原始数据
@Getter
ConcurrentHashMap<String, List<Object>> dataMap = new ConcurrentHashMap<>();
// 使用ConcurrentHashMap存储所有移动物体的最新状态
// key为物体的唯一标识如航班号value为对应的移动物体
private final ConcurrentHashMap<String, Aircraft> aircraftMap = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, SpecialVehicle> vehicleMap = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, UnmannedVehicle> unmannedVehicleMap = new ConcurrentHashMap<>();
private final DataCollectorDao dataCollectorDao;
public DataCollectorService(DataCollectorDao dataCollectorDao) {
this.dataCollectorDao = dataCollectorDao;
}
@Scheduled(fixedRateString = "${data.collector.interval}")
@Async // 异步执行
public void collectAircraftData() {
List<Aircraft> newAircrafts = dataCollectorDao.collectAircraftData(airportAircraftEndpoint, airportBaseUrl);
for (Aircraft newAircraft : newAircrafts) {
String flightNo = newAircraft.getFlightNo();
// 获取已存在的航空器如果存在
Aircraft existingAircraft = aircraftMap.get(flightNo);
if (existingAircraft != null) {
// 更新现有航空器的状态
MovementState currentState = new MovementState(
newAircraft.getCurrentPosition(),
newAircraft.getVelocity(),
newAircraft.getHeading(),
newAircraft.getTimestamp()
);
// 使用已存在航空器的历史队列
existingAircraft.setCurrentPosition(newAircraft.getCurrentPosition());
existingAircraft.setVelocity(newAircraft.getVelocity());
existingAircraft.setHeading(newAircraft.getHeading());
existingAircraft.setTimestamp(newAircraft.getTimestamp());
existingAircraft.getStateHistory().addFirst(currentState);
// 控制历史记录长度
if (existingAircraft.getStateHistory().size() > existingAircraft.MAX_HISTORY) {
existingAircraft.getStateHistory().removeLast();
}
} else {
// 新的航空器初始化历史记录
MovementState initialState = new MovementState(
newAircraft.getCurrentPosition(),
newAircraft.getVelocity(),
newAircraft.getHeading(),
newAircraft.getTimestamp()
);
newAircraft.getStateHistory().addFirst(initialState);
aircraftMap.put(flightNo, newAircraft);
}
}
// 更新数据Map用于其他服务访问
storeData(MovingObjectType.AIRCRAFT.toString(), new ArrayList<>(aircraftMap.values()));
}
@Scheduled(fixedRateString = "${data.collector.interval}")
@Async // 异步执行
public void collectVehicleData() {
List<SpecialVehicle> vehicles = dataCollectorDao.collectVehicleData(airportVehicleEndpoint, airportBaseUrl);
for (SpecialVehicle newVehicle : vehicles) {
String vehicleNo = newVehicle.getVehicleNo();
// 获取已存在的航空器如果存在
SpecialVehicle specialVehicle = vehicleMap.get(vehicleNo);
if (specialVehicle != null) {
// 更新现有航空器的状态
MovementState currentState = new MovementState(
newVehicle.getCurrentPosition(),
newVehicle.getVelocity(),
newVehicle.getHeading(),
newVehicle.getTimestamp()
);
// 使用已存在航空器的历史队列
specialVehicle.setCurrentPosition(newVehicle.getCurrentPosition());
specialVehicle.setVelocity(newVehicle.getVelocity());
specialVehicle.setHeading(newVehicle.getHeading());
specialVehicle.setTimestamp(newVehicle.getTimestamp());
specialVehicle.getStateHistory().addFirst(currentState);
// 控制历史记录长度
if (specialVehicle.getStateHistory().size() > specialVehicle.MAX_HISTORY) {
specialVehicle.getStateHistory().removeLast();
}
} else {
// 新的航空器初始化历史记录
MovementState initialState = new MovementState(
newVehicle.getCurrentPosition(),
newVehicle.getVelocity(),
newVehicle.getHeading(),
newVehicle.getTimestamp()
);
newVehicle.getStateHistory().addFirst(initialState);
vehicleMap.put(vehicleNo, newVehicle);
}
}
// 更新数据Map用于其他服务访问
storeData(MovingObjectType.SPECIAL_VEHICLE.toString(), new ArrayList<>(vehicleMap.values()));
}
@Scheduled(fixedRateString = "${data.collector.interval}")
@Async // 异步执行
public void collectVehicleLocationData() {
List<UnmannedVehicle> unmannedVehicles = dataCollectorDao.getVehicleLocationInfo();
for (UnmannedVehicle newVehicle : unmannedVehicles) {
String vehicleNo = newVehicle.getVehicleId();
// 获取已存在的航空器如果存在
UnmannedVehicle unmannedVehicle = unmannedVehicleMap.get(vehicleNo);
if (unmannedVehicle != null) {
// 更新现有航空器的状态
MovementState currentState = new MovementState(
newVehicle.getCurrentPosition(),
newVehicle.getVelocity(),
newVehicle.getHeading(),
newVehicle.getTimestamp()
);
// 使用已存在航空器的历史队列
unmannedVehicle.setCurrentPosition(newVehicle.getCurrentPosition());
unmannedVehicle.setVelocity(newVehicle.getVelocity());
unmannedVehicle.setHeading(newVehicle.getHeading());
unmannedVehicle.setTimestamp(newVehicle.getTimestamp());
unmannedVehicle.getStateHistory().addFirst(currentState);
// 控制历史记录长度
if (unmannedVehicle.getStateHistory().size() > unmannedVehicle.MAX_HISTORY) {
unmannedVehicle.getStateHistory().removeLast();
}
} else {
// 新的航空器初始化历史记录
MovementState initialState = new MovementState(
newVehicle.getCurrentPosition(),
newVehicle.getVelocity(),
newVehicle.getHeading(),
newVehicle.getTimestamp()
);
newVehicle.getStateHistory().addFirst(initialState);
unmannedVehicleMap.put(vehicleNo, newVehicle);
}
}
// 更新数据Map用于其他服务访问
storeData(MovingObjectType.UNMANNED_VEHICLE.toString(), new ArrayList<>(unmannedVehicleMap.values()));
}
private <T> void storeData(String type, List<T> data) {
dataMap.put(type, new CopyOnWriteArrayList<>(data));
}
private void validateAndUpdateTimestamp(MovementState state, LinkedList<MovementState> history) {
if (!history.isEmpty()) {
MovementState lastState = history.getFirst();
if (state.getTimestamp() <= lastState.getTimestamp()) {
log.warn("检测到时间戳乱序: current={}, last={}",
state.getTimestamp(), lastState.getTimestamp());
// 使用递增时间戳
state.setTimestamp(lastState.getTimestamp() + 1);
}
}
}
}

View File

@ -0,0 +1,88 @@
package com.dongni.collisionavoidance.dataProcessing.service;
import com.dongni.collisionavoidance.common.model.MovingObjectType;
import com.dongni.collisionavoidance.dataCollector.service.DataCollectorService;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
@Service
public class DataProcessor {
// 处理线程池
private final ExecutorService executor = Executors.newFixedThreadPool(5);
// private final RedisTemplate<String, Object> redisTemplate;
// private final MongoTemplate mongoTemplate;
private final DataCollectorService dataCollectorService;
public DataProcessor(
// RedisTemplate<String, Object> redisTemplate,
// MongoTemplate mongoTemplate,
DataCollectorService dataCollectorService) {
// this.redisTemplate = redisTemplate;
// this.mongoTemplate = mongoTemplate;
this.dataCollectorService = dataCollectorService;
}
@PostConstruct
public void init() {
executor.execute(this::processLoop);
}
private void processLoop() {
while (true) {
try {
// 获取共享的数据Map
ConcurrentHashMap<String, List<Object>> currentDataMap = dataCollectorService.getDataMap();
// 遍历所有数据类型
for (Map.Entry<String, List<Object>> entry : currentDataMap.entrySet()) {
String dataType = entry.getKey();
List<Object> dataList = entry.getValue();
if (dataList != null && !dataList.isEmpty()) {
// 根据不同的数据类型进行处理
switch (dataType) {
case "AIRCRAFT"-> processAircraftData(dataList);
case "SPECIAL_VEHICLE" -> processVehicleData(dataList);
case "UNMANNED_VEHICLE"-> processLocationData(dataList);
}
}
}
// 处理完后休眠一段时间
Thread.sleep(1000);
} catch (Exception e) {
log.error("Error in data processing loop", e);
}
}
}
// 预留的处理方法
private void processAircraftData(List<Object> dataList) {
// TODO: 实现飞机数据处理逻辑
System.out.println("processAircraftData" + dataList);
}
private void processVehicleData(List<Object> dataList) {
// TODO: 实现车辆数据处理逻辑
System.out.println("processAircraftData" + dataList);
}
private void processLocationData(List<Object> dataList) {
// TODO: 实现位置数据处理逻辑
System.out.println("processAircraftData" + dataList);
}
}

View File

@ -0,0 +1,93 @@
server:
port: 8082
spring:
application:
name: CollisionAvoidance
# Kafka配置
kafka:
bootstrap-servers: 192.168.42.128:9092
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
acks: 1
retries: 3
# 消费者配置(如果需要订阅其他服务的消息)
consumer:
group-id: data-collector-group
auto-offset-reset: latest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
properties:
spring.json.trusted.packages: "com.airport.common.model"
# Redis配置
redis:
host: localhost
port: 6379
database: 0
timeout: 10000
lettuce:
pool:
max-active: 8
max-wait: -1
max-idle: 8
min-idle: 0
key-serialization: org.springframework.data.redis.serialization.StringRedisSerializer
value-serialization: org.springframework.data.redis.serialization.Jackson2JsonRedisSerializer
# 数据采集配置
data:
collector:
interval: 1000
topics:
position: aircraft-positions
vehicle: vehicle-positions
# 机场数据源配置
airport-api:
base-url: http://localhost:8090
endpoints:
login: /login
aircraft: /openApi/getCurrentFlightPositions
vehicle: /openApi/getCurrentVehiclePositions
refresh: /refresh
auth:
username: dianxin
password: dianxin@123
# 无人车厂商数据源配置
vehicle-api:
base-url: http://127.0.0.1:31140
endpoints:
vehicle-location: /api/VehicleLocationInfo
vehicle-state: /api/VehicleStateInfo
vehicle-command: /api/VehicleCommandInfo
# MongoDB配置
mongodb:
uri: mongodb://localhost:27017/vehicle_tracking
retention:
redis-expire-seconds: 60
mongodb-days: 30
# 数据保留策略配置
# Actuator配置
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
logging:
level:
org:
springframework:
web:
client:
RestTemplate: DEBUG
http:
converter:
json: TRACE

View File

@ -0,0 +1,13 @@
package com.dongni.collisionavoidance;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class CollisionAvoidanceApplicationTests {
@Test
void contextLoads() {
}
}

View File

@ -0,0 +1,84 @@
服务器将在 http://127.0.0.1:31140 启动。
## 可用接口
1. 获取车辆定位信息
- URL: GET http://127.0.0.1:31140/api/VehicleLocationInfo
2. 获取车辆状态信息
- URL: POST http://127.0.0.1:31140/api/VehicleStateInfo
3. 发送指令接口
- URL: POST http://127.0.0.1:31140/api/VehicleCommandInfo
## API文档
启动服务器后,访问 http://127.0.0.1:31140/docs 可以查看完整的API文档。
让我为您分析这两个mock服务器的数据
### 1. mock_server.py主要mock服务器
#### 飞机数据:
- 1架飞机`AC001`
- 初始位置从T7点出发
- 运动路线T7 -> T11到达后返回起点
- 速度36 km/h
#### 车辆数据:
总共3辆车
1. `QN001`无人车1
- 路线T1 -> T2 -> T4到达后返回T1
- 速度默认18 km/h
2. `QN002`无人车2
- 路线T12 -> T8到达后返回T12
- 速度默认18 km/h
3. `TQ001`(特勤车)
- 路线T4 -> T2 -> T3到达后返回T4
- 速度默认18 km/h
#### 红绿灯数据:
两个路口的红绿灯:
1. `TL001`西路口T2点
2. `TL002`东路口T6点
### 2. vehicle_mock_server.py车辆mock服务器
这是一个独立的车辆模拟服务器:
- 随机生成5辆车编号AT001-AT005
- 在两条预定义路线上随机行驶
- 速度5-8米/秒
- 位置实时更新
### 数据传递方式:
1. mock_server.py 提供以下API
```python
- /openApi/getCurrentFlightPositions # 获取飞机位置
- /openApi/getCurrentVehiclePositions # 获取车辆位置
- /openApi/getTrafficLightSignals # 获取红绿灯状态
- /openApi/getVehicleStatus # 获取车辆状态
- /login # 登录认证
```
2. vehicle_mock_server.py 提供以下API
```python
- /api/VehicleLocationInfo # 获取车辆位置
- /api/VehicleStateInfo # 获取车辆状态
- /api/VehicleCommandInfo # 接收车辆指令
```
### 总结:
这两个mock服务器模拟了不同场景
1. mock_server.py 模拟机场场景包含1架飞机、3辆车和2个红绿灯路口的完整交通系统
2. vehicle_mock_server.py 则是一个独立的车辆模拟系统随机生成5辆车的运动数据
这两个服务器运行在不同端口:
- mock_server.py: 8081
- vehicle_mock_server.py: 31140
它们可以同时运行,为测试提供不同的数据源。

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,251 @@
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional, Dict
import uvicorn
import random
import uuid
from datetime import datetime
from enum import Enum
import math
import logging
import time
import threading
# 设置日志
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
app = FastAPI()
# 枚举定义
class CommandType(str, Enum):
ALERT = "ALERT"
SIGNAL = "SIGNAL"
WARNING = "WARNING"
RESUME = "RESUME"
PARKING = "PARKING"
class CommandReason(str, Enum):
TRAFFIC_LIGHT = "TRAFFIC_LIGHT"
AIRCRAFT_CROSSING = "AIRCRAFT_CROSSING"
SPECIAL_VEHICLE = "SPECIAL_VEHICLE"
AIRCRAFT_PUSH = "AIRCRAFT_PUSH"
RESUME_TRAFFIC = "RESUME_TRAFFIC"
PARKING_SIDE = "PARKING_SIDE"
class SignalState(str, Enum):
RED = "RED"
GREEN = "GREEN"
# 数据模型
class VehicleLocationInfo(BaseModel):
transId: str
timestamp: int
vehicleId: str
longitude: float
latitude: float
direction: float
speed: float
class VehicleStateInfo(BaseModel):
transId: str
timestamp: int
vehicleId: str
loginStatus: bool
faultInfo: List[str]
activeSafety: bool
rc: bool
command: int
airportInfo: List[str]
vehicleMode: int
gearState: int
chassisReady: bool
collisionStatus: bool
clearance: int
turnSignalStatus: int
pointCloud: List[int]
class CommandRequest(BaseModel):
transId: str
timestamp: int
vehicleId: str
commandType: CommandType
commandReason: CommandReason
signalState: Optional[SignalState]
intersectionId: Optional[str]
latitude: float
longitude: float
relativeSpeed: Optional[float]
relativeMotionX: Optional[float]
relativeMotionY: Optional[float]
minDistance: Optional[float]
class CommandResponse(BaseModel):
code: int
msg: str
transId: str
timestamp: int
# 预定义的路线点(以机场为例的一些路线)
ROUTES = {
"ROUTE1": [
(121.805, 31.151), # 起点
(121.807, 31.152), # 转弯点1
(121.809, 31.153), # 转弯点2
(121.811, 31.154), # 终点
],
"ROUTE2": [
(121.806, 31.155),
(121.808, 31.156),
(121.810, 31.157),
(121.812, 31.158),
]
}
# 存储车辆当前状态
vehicle_states: Dict[str, dict] = {}
class TimeStampGenerator:
def __init__(self):
self.last_timestamp = int(time.time() * 1000)
self.sequence = 0
self.lock = threading.Lock()
def next_timestamp(self):
with self.lock:
current = int(time.time() * 1000)
if current == self.last_timestamp:
self.sequence += 1
else:
self.sequence = 0
self.last_timestamp = current
return self.last_timestamp + self.sequence
timestamp_generator = TimeStampGenerator()
def calculate_direction(p1, p2):
"""计算两点之间的方向角(弧度)"""
dx = p2[0] - p1[0]
dy = p2[1] - p1[1]
return math.atan2(dy, dx)
def calculate_next_position(current_pos, target_pos, speed):
"""计算下一个位置点"""
# 将速度从米/秒转换为经纬度增量
speed_factor = speed * 0.00001 # 简化的转换因子
dx = target_pos[0] - current_pos[0]
dy = target_pos[1] - current_pos[1]
distance = math.sqrt(dx*dx + dy*dy)
if distance < speed_factor:
return target_pos
ratio = speed_factor / distance
new_x = current_pos[0] + dx * ratio
new_y = current_pos[1] + dy * ratio
return (new_x, new_y)
def get_or_create_vehicle_state(vehicle_id: str) -> dict:
"""获取或创建车辆状态"""
if vehicle_id not in vehicle_states:
route = random.choice(list(ROUTES.values()))
vehicle_states[vehicle_id] = {
'current_pos': route[0],
'route': route,
'route_index': 0,
'speed': random.uniform(5.0, 8.0) # 5-8米/秒
}
return vehicle_states[vehicle_id]
def update_vehicle_position(vehicle_id: str, state: dict) -> tuple:
"""更新车辆位置"""
current_pos = state['current_pos']
route = state['route']
route_index = state['route_index']
if route_index >= len(route) - 1:
route_index = 0
target_pos = route[route_index + 1]
next_pos = calculate_next_position(current_pos, target_pos, state['speed'])
# 如果到达目标点,移动到下一个路线点
if next_pos == target_pos:
route_index += 1
if route_index >= len(route) - 1:
route_index = 0
state['current_pos'] = next_pos
state['route_index'] = route_index
direction = calculate_direction(current_pos, target_pos)
return next_pos, direction
def generate_vehicle_location():
vehicle_id = f"AT{random.randint(1, 5):03d}" # 限制车辆数量为5辆
state = get_or_create_vehicle_state(vehicle_id)
position, direction = update_vehicle_position(vehicle_id, state)
vehicle_info = VehicleLocationInfo(
transId=str(uuid.uuid4()),
timestamp=timestamp_generator.next_timestamp(),
vehicleId=vehicle_id,
longitude=position[0],
latitude=position[1],
direction=direction,
speed=state['speed']
)
logger.info(f"Vehicle {vehicle_id} position updated - Longitude: {position[0]:.6f}, "
f"Latitude: {position[1]:.6f}, Direction: {direction:.6f} rad, "
f"Speed: {state['speed']:.2f} m/s")
return vehicle_info
def generate_vehicle_state():
return VehicleStateInfo(
transId=str(uuid.uuid4()),
timestamp=int(datetime.now().timestamp() * 1000),
vehicleId=f"AT{random.randint(1, 999):03d}",
loginStatus=random.choice([True, False]),
faultInfo=[],
activeSafety=random.choice([True, False]),
rc=False,
command=0,
airportInfo=[],
vehicleMode=random.randint(1, 5),
gearState=random.randint(1, 5),
chassisReady=True,
collisionStatus=False,
clearance=random.randint(0, 1),
turnSignalStatus=random.randint(0, 3),
pointCloud=[]
)
# API端点
@app.get("/api/VehicleLocationInfo", response_model=List[VehicleLocationInfo])
async def get_vehicle_location():
vehicles = [generate_vehicle_location() for _ in range(5)] # 固定生成5辆车的数据
logger.info(f"Returning location data for {len(vehicles)} vehicles")
return vehicles
@app.post("/api/VehicleStateInfo", response_model=List[VehicleStateInfo])
async def get_vehicle_state():
return [generate_vehicle_state() for _ in range(random.randint(1, 5))]
@app.post("/api/VehicleCommandInfo", response_model=CommandResponse)
async def vehicle_command(command: CommandRequest):
return CommandResponse(
code=200,
msg="success",
transId=command.transId,
timestamp=int(datetime.now().timestamp() * 1000)
)
if __name__ == "__main__":
logger.info("Starting mock vehicle server on port 31140...")
uvicorn.run(app, host="127.0.0.1", port=31140)