commit b2856d00ee399535f91267083df1750d136ca257 Author: MonHun Date: Sun Feb 11 22:17:03 2024 +0900 win 0211 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..bf82ff0 Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..ca5ab4b --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# 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 +# +# https://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. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar diff --git a/cfg/udpsocket.xml b/cfg/udpsocket.xml new file mode 100644 index 0000000..2c9bb7c --- /dev/null +++ b/cfg/udpsocket.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/ipworks/local/1.0.0/ipworks-local-1.0.0.jar b/lib/ipworks/local/1.0.0/ipworks-local-1.0.0.jar new file mode 100644 index 0000000..557d507 Binary files /dev/null and b/lib/ipworks/local/1.0.0/ipworks-local-1.0.0.jar differ diff --git a/lib/state-spring-boot-starter-1.0.3.jar b/lib/state-spring-boot-starter-1.0.3.jar new file mode 100644 index 0000000..4f5a7df Binary files /dev/null and b/lib/state-spring-boot-starter-1.0.3.jar differ diff --git a/lib/tibero-jdbc-7.0.0.jar b/lib/tibero-jdbc-7.0.0.jar new file mode 100644 index 0000000..69b9029 Binary files /dev/null and b/lib/tibero-jdbc-7.0.0.jar differ diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..1d8ab01 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,188 @@ +@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 https://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 Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f818428 --- /dev/null +++ b/pom.xml @@ -0,0 +1,122 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.8 + + + kr.gmtc.gw + + asderecv + 0.0.1-SNAPSHOT + EyeGW_AsdeRecv + Demo project for Spring Boot + + + + local-repository + local-repository + file:${project.basedir}/lib + + + + + 1.8 + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + + kr.gmt.so + state-spring-boot-starter + 1.0.3 + system + ${basedir}/lib/state-spring-boot-starter-1.0.3.jar + + + + + + org.projectlombok + lombok + 1.18.24 + provided + + + + + + + + ipworks.local + ipworks-local-1.0.0 + system + 1.0.0 + ${basedir}/lib/ipworks/local/1.0.0/ipworks-local-1.0.0.jar + + + + + + + + org.yaml + snakeyaml + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + com.fasterxml.jackson.core + jackson-databind + + + + + + EyeGW_AsdeRecv-0.0.1 + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + + + diff --git a/src/main/java/kr/gmtc/gw/asderecv/AsdeRecvApplication.java b/src/main/java/kr/gmtc/gw/asderecv/AsdeRecvApplication.java new file mode 100644 index 0000000..cf7089b --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/AsdeRecvApplication.java @@ -0,0 +1,49 @@ +package kr.gmtc.gw.asderecv; + +import java.time.ZoneId; +import java.util.TimeZone; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.ApplicationPidFileWriter; +import org.springframework.boot.system.ApplicationHome; +import org.springframework.scheduling.annotation.EnableScheduling; + +@EnableScheduling +@SpringBootApplication +public class AsdeRecvApplication { + // implements CommandLineRunner + // @Autowired + // private ApplicationContext appContext; + + public static void main(String[] args) { + //SpringApplication.run(SocketApplication.class, args); + ApplicationHome home = new ApplicationHome(AsdeRecvApplication.class); + String root = home.getDir().getPath(); + + System.setProperty("user.dir", root); + + TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("UTC"))); + + Thread.currentThread().setName("JVM - Main"); + + SpringApplication springApplication = new SpringApplication(AsdeRecvApplication.class); + + springApplication.addListeners(new ApplicationPidFileWriter("./application.pid")); + springApplication.run(args); + + } + + // @Override + // public void run(String...args) throws Exception { + + // String [] beans = appContext.getBeanDefinitionNames(); + // Arrays.sort(beans); + // for(String bean : beans ){ + // System.out.println(bean); + // } + + + // } + +} diff --git a/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/AsterixList_Category.java b/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/AsterixList_Category.java new file mode 100644 index 0000000..d283d0a --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/AsterixList_Category.java @@ -0,0 +1,26 @@ +/**************************************************************************** + + [asterixList.xml] spec file list + +******************************************************************************/ +package kr.gmtc.gw.asderecv.asde.asterixSpec; + +import java.util.*; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "AsterixListCategory") +public class AsterixList_Category +{ + private List asterixFile; + + @XmlElement(name = "AsterixSpecFile") + public void setAsterixs(List asterixFile) { + this.asterixFile = asterixFile; + } + + public List getAsterixs() { + return this.asterixFile; + } +} \ No newline at end of file diff --git a/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/AsterixList_SpecFile.java b/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/AsterixList_SpecFile.java new file mode 100644 index 0000000..66d1ce0 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/AsterixList_SpecFile.java @@ -0,0 +1,34 @@ +/**************************************************************************** + + [asterixList.xml] spec file 경로 정보 + +******************************************************************************/ +package kr.gmtc.gw.asderecv.asde.asterixSpec; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "AsterixSpecFile") +public class AsterixList_SpecFile +{ + private String category; + private String file; + + @XmlAttribute(name = "category") + public void setCategory(String category) { + this.category = category; + } + + public String getCategory() { + return this.category; + } + + @XmlAttribute(name = "file") + public void setFile(String file) { + this.file = file; + } + + public String getFile() { + return this.file; + } +} \ No newline at end of file diff --git a/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/AsterixSpecLoader.java b/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/AsterixSpecLoader.java new file mode 100644 index 0000000..8273ea6 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/AsterixSpecLoader.java @@ -0,0 +1,129 @@ +package kr.gmtc.gw.asderecv.asde.asterixSpec; + +import java.io.IOException; +import java.io.InputStream; +import java.util.LinkedHashMap; +import java.util.List; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.Unmarshaller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import kr.gmtc.gw.asderecv.asde.asterixSpec.vo.AsterixSpecVO; +import kr.gmtc.gw.asderecv.asde.utils.YamlFileLoader; + +/**************************************************************************** + + asterixList.xml에 정의 되어 있는 모든 spec 파일을 로드하여 specMap에 가지고있고 + instance를 생성한 클레스에서 getSpec()을 통해 로드된 spec을 가져갈 수 있음. + + 사용예) + String sCatNo = "10"; + AsterixSpec asterixSpec = new AsterixSpec(); + SpecDepth1Category currentSpec = asterixSpec.getSpec(sCatNo); + + +******************************************************************************/ + +public class AsterixSpecLoader +{ + private Logger logger; + + // category별 spec + private LinkedHashMap specMap; + + public AsterixSpecLoader() { + initCategory(); + } + + private void initCategory() + { + + this.specMap = new LinkedHashMap(); + + AsterixList_Category target; + + this.logger = LoggerFactory.getLogger(this.getClass()); + + logger.info("Initializing.. "); + // 분석해야 할 Asterix Spec 파일 목록 로드 (resource/asterixList.xml) + try + { + JAXBContext context = JAXBContext.newInstance(AsterixList_Category.class); + Unmarshaller umarshaller = context.createUnmarshaller(); + InputStream is = this.getClass().getClassLoader().getResourceAsStream("asterixList.xml"); + target = (AsterixList_Category)umarshaller.unmarshal(is); + } + catch(Exception e) + { + logger.error("initCategory- Spec List 로드 실패"); + return; + } + + if(target == null) { + logger.error("initCategory- Spec List 로드 실패(target=null)"); + return; + } + + List spacList = target.getAsterixs(); + + // category별로 상이한 spec내용을 로드 (cat-10, cat-11, ...) + for(int i=0;i dataitem; + + public String getCat() { + return cat; + } + public void setCat(String cat) { + this.cat = cat; + } + public String getVer() { + return ver; + } + public void setVer(String ver) { + this.ver = ver; + } + + @JsonProperty("dataitem") + public List getDataitem() { + return dataitem; + } + + @JsonProperty("dataitem") + public void setDataitem(List dataitem) { + this.dataitem = dataitem; + } + + + +} \ No newline at end of file diff --git a/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/vo/AsterixSpecVO_Codes.java b/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/vo/AsterixSpecVO_Codes.java new file mode 100644 index 0000000..48a10da --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/vo/AsterixSpecVO_Codes.java @@ -0,0 +1,37 @@ +/**************************************************************************** + + spec(yml)파일의 dataitem.Itemno.structure.name 하위 노드 "codes..." + +******************************************************************************/ +package kr.gmtc.gw.asderecv.asde.asterixSpec.vo; + +import java.util.*; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class AsterixSpecVO_Codes +{ + + private Map codes = new HashMap(); + + + @JsonAnyGetter + public Map getCodes() { + return this.codes; + } + + + public void setCodes(Map codes) { + this.codes = codes; + } + + @JsonAnySetter + public void addValue(String key, String value) { + this.codes.put(key, value); + } + + +} \ No newline at end of file diff --git a/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/vo/AsterixSpecVO_DataItem.java b/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/vo/AsterixSpecVO_DataItem.java new file mode 100644 index 0000000..cd132f4 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/vo/AsterixSpecVO_DataItem.java @@ -0,0 +1,94 @@ +/**************************************************************************** + + spec(yml)파일의 dataitem 하위 노드 "-Itemno..." + +******************************************************************************/ +package kr.gmtc.gw.asderecv.asde.asterixSpec.vo; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import java.io.Serializable; +import java.util.List; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class AsterixSpecVO_DataItem implements Serializable +{ + + private static final long serialVersionUID = -11726258863773521L; + + private String itemno; + private String frn; + private String rule; + private String itemdesc; + private String definition; + private String octet; + + private String format; + + private List structure; + + private String dataItemNote; + + public String getItemno() { + return itemno; + } + public void setItemno(String itemno) { + this.itemno = itemno; + } + public String getFrn() { + return frn; + } + public void setFrn(String frn) { + this.frn = frn; + } + public String getItemdesc() { + return itemdesc; + } + public void setItemdesc(String itemdesc) { + this.itemdesc = itemdesc; + } + public String getDefinition() { + return definition; + } + public void setDefinition(String definition) { + this.definition = definition; + } + public String getOctet() { + return octet; + } + public void setOctet(String octet) { + this.octet = octet; + } + public String getFormat() { + return format; + } + public void setFormat(String format) { + this.format = format; + } + public List getStructure() { + return structure; + } + public void setStructure(List structure) { + this.structure = structure; + } + public String getDataItemNote() { + return dataItemNote; + } + public void setDataItemNote(String dataItemNote) { + this.dataItemNote = dataItemNote; + } + public String getRule() { + + if(this.rule == null) { + return rule = ""; + } + + return rule; + } + public void setRule(String rule) { + this.rule = rule; + } + + + +} \ No newline at end of file diff --git a/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/vo/AsterixSpecVO_Structure.java b/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/vo/AsterixSpecVO_Structure.java new file mode 100644 index 0000000..3bbee26 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/asde/asterixSpec/vo/AsterixSpecVO_Structure.java @@ -0,0 +1,121 @@ +/**************************************************************************** + + spec(yml)파일의 dataitem.Itemno 하위 노드 "structure..." + +******************************************************************************/ +package kr.gmtc.gw.asderecv.asde.asterixSpec.vo; + +import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class AsterixSpecVO_Structure implements Serializable +{ + + private static final long serialVersionUID = -4804047491424671870L; + + private String name; + private String procseq; + + private String frombit; + private String tobit; + private String desc; + private String resolution; + private String datatype; + private String unit; + private AsterixSpecVO_Codes codes; + + private String min; + private String max; + + private String serviceFieldName; + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + public String getProcseq() { + return procseq; + } + public void setProcseq(String procseq) { + this.procseq = procseq; + } + public String getFrombit() { + return frombit; + } + public void setFrombit(String frombit) { + this.frombit = frombit; + } + public String getTobit() { + + if(this.tobit == null) { + return this.frombit; + }else { + return tobit; + } + + } + public void setTobit(String tobit) { + this.tobit = tobit; + } + public String getDesc() { + return desc; + } + public void setDesc(String desc) { + this.desc = desc; + } + public String getResolution() { + return resolution; + } + public void setResolution(String resolution) { + this.resolution = resolution; + } + public String getDatatype() { + if(this.datatype == null) { + return datatype = "uint"; + }else { + return datatype; + } + + } + public void setDatatype(String datatype) { + this.datatype = datatype; + } + public String getUnit() { + return unit; + } + public void setUnit(String unit) { + this.unit = unit; + } + public AsterixSpecVO_Codes getCodes() { + return codes; + } + public void setCodes(AsterixSpecVO_Codes codes) { + this.codes = codes; + } + public String getMin() { + return min; + } + public void setMin(String min) { + this.min = min; + } + public String getMax() { + return max; + } + public void setMax(String max) { + this.max = max; + } + public String getServiceFieldName() { + return serviceFieldName; + } + public void setServiceFieldName(String serviceFieldName) { + this.serviceFieldName = serviceFieldName; + } + + + + +} \ No newline at end of file diff --git a/src/main/java/kr/gmtc/gw/asderecv/asde/parser/AsterixParser.java b/src/main/java/kr/gmtc/gw/asderecv/asde/parser/AsterixParser.java new file mode 100644 index 0000000..cb648c8 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/asde/parser/AsterixParser.java @@ -0,0 +1,1007 @@ +package kr.gmtc.gw.asderecv.asde.parser; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import kr.gmtc.gw.asderecv.asde.asterixSpec.AsterixSpecLoader; +import kr.gmtc.gw.asderecv.asde.asterixSpec.vo.AsterixSpecVO; +import kr.gmtc.gw.asderecv.asde.asterixSpec.vo.AsterixSpecVO_Codes; +import kr.gmtc.gw.asderecv.asde.asterixSpec.vo.AsterixSpecVO_DataItem; +import kr.gmtc.gw.asderecv.asde.asterixSpec.vo.AsterixSpecVO_Structure; +import kr.gmtc.gw.asderecv.asde.utils.AsterixParserUtils; +import kr.gmtc.gw.asderecv.asde.utils.FieldNameReflectionUtils; +import kr.gmtc.gw.asderecv.rest.vo.ServiceAsdeData; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + + +public class AsterixParser { + + private Logger logger; + + private AsterixSpecLoader asterixSpec; + + private List serviceMap; + private AsterixSpecVO currentSpec; + + private LinkedHashMap resultMap ; + private ServiceAsdeData scMap ; + + private AsterixParserUtils apUtils; + private FieldNameReflectionUtils fieldUtils; + + private DecimalFormat decFormat = new DecimalFormat("#.#########"); + + private String debugLogMode = "none"; + + /***************************************** Constructor & Main Method *****************************************/ + + public AsterixParser() + { + initParser(); + } + + public AsterixParser( List serviceMap) { + + initParser(); + this.serviceMap = serviceMap; + + } + + private void initParser() { + + logger = LoggerFactory.getLogger(AsterixParser.class); + + fieldUtils = new FieldNameReflectionUtils(); + apUtils = new AsterixParserUtils(); + asterixSpec = new AsterixSpecLoader(); + + } + + public List> parse(byte[] data, String sRecvTime) { + String sCatNo = null; + try + { + if(data.length < 5) return null; + + // 바이트 값을 16진수(0xff, 255)와 비트연산하여 정수로 변환 + sCatNo = String.valueOf(data[0] & 0xff); + + // 데이터부의 category와 일치하는 스펙 가져오기 + currentSpec = asterixSpec.getSpec(sCatNo); + + if(currentSpec == null ) { + logger.error("Asterix Spec이 정의되어 있지 않음. [수신메시지의 category:" + sCatNo + "]"); + return null; + }else { + if (currentSpec.getDataitem().isEmpty()) { + logger.error("Asterix Spec yml파일에 구문 오류가 있음. [수신메시지의 category:" + sCatNo + "]"); + } + } + + // 데이터 블럭의 분석 결과 + List> result_list = new ArrayList>(); + + // 수신받은 데이터 총길이 + int total_len = data.length; + + // 데이터블럭 사이즈 (데이터 블럭(1) : 데이터 패킷(N)) + byte[] blSize = new byte[2]; + + // 분석 처리 대상 블럭 + byte[] blockData = null; + + // 한번에 여러개의 블럭을 수신 받은경우 반복하여 처리 하기위한 블럭단위 초기 인덱스 + // 1개 블럭의 마지막 인덱스를 저장 + int index = 0; + + // 데이터 블록에 정의되어 있는 총 길이 만큼 잘라서 분석하고 (real_data) + // 남아 있는 데이터가 있다면 새로운 데이터블록이므로 반복하여 처리 + while(total_len > index) + { +// int iCatNo = data[index] & 0xff; + + // 데이터 블록의 총 길이(2옥텟) + blSize[0] = data[index+1]; + blSize[1] = data[index+2]; + + int iDataBlockSize = apUtils.unsignedToInt(blSize); + int iBlockStartPos = 3; + +// logger.error("[AsterixParser:parse] 수신 데이터 총 길이:" + total_len + " / 분석대상 블록 길이" + iDataBlockSize, LogLevelType.LOG_DEBUG, "AllLog"); + + if(iDataBlockSize <= 0) return result_list; + + // 데이터 블록의 총 길이만큼 초기화 + blockData = new byte[iDataBlockSize]; + + try + { + if((index+iDataBlockSize) > total_len) iDataBlockSize = total_len-index; + + /* System.arraycopy(src, srcPos, dest, destPos, length); + src - 원본 배열 + srcPos - 원본 배열의 복사 시작 위치 + dest - 복사할 배열 + destPost - 복사할 배열의 복사 시작 위치 + length - 복사할 요소의 개수 + * */ + System.arraycopy(data, index + iBlockStartPos, blockData, 0, iDataBlockSize - iBlockStartPos); + + List> list = parseOneBlock(blockData, sRecvTime); + + if(list != null) result_list.addAll(list); + } + catch(Exception e) + { + e.printStackTrace(); + logger.error("Exception:" + e); + return result_list; + } + + index += iDataBlockSize; + } + + return result_list; + + } + catch(Exception e) + { + logger.error("수신메시지 분석 실패 [CAT " + sCatNo + "]"); + return null; + } + } + + + private List> parseOneBlock(byte[] blockData, String sRecvTime) throws Exception { + + int body_len = blockData.length; + + List> result = new ArrayList>(); + + Map mapUAP; + + int index = 0; + int index_before = 0; + int idx; + + while(index < body_len-1) + { + + int to_read = body_len - index; + mapUAP = new LinkedHashMap(); + + // UAP 체크 + for(idx=0; idx it = mapUAP.keySet().iterator(); + scMap = new ServiceAsdeData(); + + // 카테고리유형 셋팅 + fieldUtils.setItem(scMap, "cat_ty", currentSpec.getCat()); + + //수신시각 셋팅 + fieldUtils.setItem(scMap, "recptn_dt", sRecvTime); + + int result_index = 0; + resultMap = new LinkedHashMap(); + while(it.hasNext()) + { + + Integer frn = it.next(); + AsterixSpecVO_DataItem di = mapUAP.get(frn); + if(di == null) continue; + + //resultMap = new LinkedHashMap(); + result_index = makeResult(di, byteRecodeData, result_index); + if(result_index < 0) break; + + } + + if(resultMap != null && resultMap.size() > 0) + { + + if(debugLogMode.equals("line")) + logger.debug("parging 결과 :" + resultMap.toString()); + + result.add(resultMap); + + // 서비스용 map에 적재하되 식별자가 없는 데이터는 스킵 + // key 값으로 사용할 수 있는 필드에 값이 있는 경우만 + if( filterSvcData(scMap) ) { + CalcScvData(resultMap); + serviceMap.add(scMap); + } + + } + + index_before = index; + + index = index + iUAPoffset + result_index; + + // log + byte[] bLogByte = new byte[index - index_before]; + System.arraycopy(blockData, index_before, bLogByte, 0, index - index_before); +// logger.error("[AsterixParser:parseOneBlock] packet [CNT :" + parserCnt + ", Hex Data :" + apUtils.cnvBytesToHex(bLogByte) + "]", LogLevelType.LOG_DEBUG, "AllLog"); + + if(result_index < 0) break; + + } + + return result; + } + + /***************************************** Make Result *****************************************/ + + private void CalcScvData(LinkedHashMap resMap) { + + double dX = apUtils.parseDoubleDef(resMap.get("X-COMPONENT")); + double dY = apUtils.parseDoubleDef(resMap.get("Y-COMPONENT")); + int iXm = apUtils.parseIntDef(resMap.get("XM")); + int iYm = apUtils.parseIntDef(resMap.get("YM")); + double iVx = apUtils.parseDoubleDef(resMap.get("VX")); + double iVy = apUtils.parseDoubleDef(resMap.get("VY")); + double dDistNM = 1; + double dAzimuth = 0; + double dSpeed; + int iMax = 8191; + + Map posMap; + + // if(resMap.get("CALLSIGN") != null && resMap.get("CALLSIGN").equals("CAL160")){ + // String dafdf = ""; + // } + + // 위경도 변환 (x,y -> wgs84) + if(dX != 0 || dY != 0) { + if( !(iXm == 0 && iYm == 0) ){ + + if (iXm == 1) { + if(dX > 0) dX = dX + iMax; + else if(dX < 0) dX = dX - iMax; + else dX = 0; + } + + if (iYm == 1) { + if(dY > 0) dY = dY + iMax; + else if(dY < 0) dY = dY - iMax; + else dY = 0; + } + + } + + dDistNM = Math.sqrt(dX * dX + dY * dY) / 1852.0; // m >> NM + dAzimuth = apUtils.calcAngleDegree(0, 0, dX, dY); + + posMap = apUtils.calcDistanceAzimuth(apUtils.CONST_BASE_LAT, apUtils.CONST_BASE_LON, dDistNM, dAzimuth); + + scMap.setLat(String.format("%.14f", posMap.get("lat"))); + scMap.setLon(String.format("%.14f", posMap.get("lon"))); + //scMap.setCos(String.format("%.2f", dAzimuth)); + } + + // 속도 계산 +// if(resMap.get("cat_ty").equals("11")){ +// dSpeed = Math.sqrt(Math.pow(iVx, 2) + Math.pow(iVy, 2)); +// } +// else if(resMap.get("cat_ty").equals("10")){ +// +// ) + dSpeed = Math.sqrt(Math.pow(iVx, 2) + Math.pow(iVy, 2)); + + + dSpeed = dSpeed * 3.6; // m/s >> km/h + + //SX(Vx, 16) = 0 or SY(Vy, 8) = 0 if VX >= 0 or VY >= 0 + + + fieldUtils.setItem(scMap, "spd", String.format("%.2f", dSpeed)); +// fieldUtils.setItem(scMap, "spd_kts", String.format("%.2f", dSpeed * 0.5399570136727677)); + + // 방향 계산 + double dir = apUtils.calcDirection(iVx, iVy); + fieldUtils.setItem(scMap, "cos", String.format("%.2f", dir)); + + + // Cat-11은 자체 필드(resMap->LAT값 체크) 사용하되 값이 없으면 계산한 위치값 셋팅 + if( !(resMap.get("LAT") == null || resMap.get("LAT").equals("")) ){ + fieldUtils.setItem(scMap, "lat", resMap.get("LAT")); + fieldUtils.setItem(scMap, "lon", resMap.get("LON")); + + }else{ + fieldUtils.setItem(scMap, "lat", scMap.getLat()); + fieldUtils.setItem(scMap, "lon", scMap.getLon()); + } + + // fieldUtils.setItem(scMap, "posx", dX + ""); + // fieldUtils.setItem(scMap, "posy", dY + ""); + + //cat10 alt + if( !(resMap.get("FL") == null || resMap.get("FL").equals("")) ){ + + // scMap.setAlt(String.valueOf(apUtils.parseDoubleDef(resMap.get("FL")))); + + String sTmpAlt = ""; + double dalt = apUtils.parseDoubleDef(resMap.get("FL")); + + //if(dalt > 160 || dalt < 0.7) dalt = 0; + if(dalt > 160 ) dalt = 0; + if(dSpeed <= 100) dalt = 0; + + + sTmpAlt = String.valueOf(dalt * 100); // FL -> ft + + scMap.setAlt(sTmpAlt); + + } + + //cat11 alt + if( !(resMap.get("MFL") == null || resMap.get("MFL").equals("")) ){ + + String sTmpAlt = ""; + double dalt = apUtils.parseDoubleDef(resMap.get("MFL")); + + if(dalt > 160 || dalt < 0.7) dalt = 0; + + sTmpAlt = String.valueOf(dalt * 100); // FL -> ft + + scMap.setAlt(sTmpAlt); + } + + // scMap.setCarty((String) resMap.get("VFI")); + + + } + + private int makeResult(AsterixSpecVO_DataItem di, byte[] data, int index) { + + if(di == null || data == null || index < 0) return -1; + + int iRetIndex = -1; + + String sFrn = di.getFrn(); + int iSize = Integer.parseInt(di.getOctet()); + List st = di.getStructure(); + +// if(resultMap == null) resultMap = new LinkedHashMap(); + if(scMap == null) scMap = new ServiceAsdeData(); + + String sFormat = di.getFormat().trim(); + + if(iSize < 0) return -1; + if(data.length - index < iSize) { + iSize = data.length - index; + } + + if(di.getItemno().equals("SPF")) iRetIndex = makeResult_End(data, index); + else if(di.getRule().equals("X")) iRetIndex = makeResult_NeverSent(di, data, index, iSize); + else if(sFormat.equalsIgnoreCase("fixed")) iRetIndex = makeResult_Fixed(data, index, iSize, st); + else if(sFormat.equalsIgnoreCase("compound")) iRetIndex = makeResult_Compound(data, index, sFrn); + else if(sFormat.equalsIgnoreCase("variable")) iRetIndex = makeResult_Variable(data, index, sFrn); + else if(sFormat.equalsIgnoreCase("repetitive")) iRetIndex = makeResult_Repetitive(data, index, di); + else iRetIndex = -1; + + printDebugLog(data, index, iRetIndex, di); + + return iRetIndex; + + } + + private int makeResult_Fixed(byte[] data, int index, int iSize, List itemStrucs) { + + int size = iSize; + + byte[] bTargetItem = new byte[size]; + + System.arraycopy(data, index, bTargetItem, 0, size); + + index += size; + + for(int i=0; i>> 8 - (frombit - tobit + 1) ; + + no = (double)number; + } + + } + else + { + + int byte_size = to_octet - from_octet; + + if(to_octet != from_octet) byte_size += 1; + bnum = new byte[byte_size]; + System.arraycopy(bTargetItem, from_octet-1, bnum, 0, byte_size); + + is_string = false; + + if(dataType.trim().equalsIgnoreCase("uint")) { + + if(bnum.length > 4) { + logger.error("bnum.length:" + bnum.length ); + }else { + no = apUtils.unsignedToInt(bnum); + } + + + } + else if(dataType.trim().equalsIgnoreCase("int")) + { +// if(bit_length < 16 && bit_length % 16 != 0) +// { +// int shift = 16 - bit_length - 1; +// bnum[0] = (byte)(bnum[0] << shift); +// bnum[0] = (byte)(bnum[0] & 0xff); +// bnum[0] = (byte)(bnum[0] >>> shift); +// bnum[0] = (byte)(bnum[0] & 0xff); +// +// } + no = apUtils.signedToInt(bnum); + } + else if(dataType.trim().equalsIgnoreCase("signbit")) + { + no = apUtils.signedToInt_withSignBit(bnum); + } + else if(dataType.trim().equalsIgnoreCase("octal")) + { + sResultValue = apUtils.makeOctal(bnum); + is_string = true; + } + else if(dataType.trim().equalsIgnoreCase("6bitschar")) + { + sResultValue = apUtils.makeICAOCode(bnum); + is_string = true; + } + else if(dataType.trim().equalsIgnoreCase("string")) + { + sResultValue = apUtils.byteArrayToString(bnum); + is_string = true; + + } + else if(dataType.trim().equalsIgnoreCase("ascii")) + { + sResultValue = apUtils.makeAscii(bnum); + is_string = true; + } + else sResultValue = ""; + } + + if(!is_string) + { + no *= Double.parseDouble(scale); + + // if(num_min != null){ + // if(no < Integer.parseInt(num_min)){ + // no = Integer.parseInt(num_min); + // } + // } + + // if(num_max != null){ + // if(no > Integer.parseInt(num_max)){ + // no = Integer.parseInt(num_max); + // } + // } + + sResultValue = String.valueOf(decFormat.format(no)); + } + } + else + { + if(from_octet == to_octet) + { + int number = bTargetItem[to_octet - 1] & 0xff; +// int shift = (8*size) - frombit; +// number = number << shift; +// number = number & ( 0xff - shift ); +// number = number >>> (8 - ((size*8)-(tobit+shift))); + int from8bit = frombit%8; + if(from8bit==0) from8bit = 8; + + int shift = 8 - from8bit; + number = number << shift; + number = number & 0xff; + number = number >>> 8 - (frombit - tobit + 1) ; + + sResultValue = Integer.toString(number); + + } + else + { + + int byte_size = to_octet - from_octet; + + if(to_octet != from_octet) byte_size += 1; + bnum = new byte[byte_size]; + System.arraycopy(bTargetItem, from_octet-1, bnum, 0, byte_size); + + if(bnum.length > 4) { + logger.error("bnum.length:" + bnum.length ); + sResultValue = ""; + }else { + no = apUtils.unsignedToInt(bnum); + sResultValue = String.valueOf(no); + } + + } + } + + resultMap.put(name, sResultValue); + if(struc.getServiceFieldName() != null) { + inputServiceField(struc, sResultValue); + + } + + } + + return index; + } + + private int makeResult_Compound(byte[] data, int index, String sFrn) { + + try + { + String di_frn = sFrn; + int idx = 0; + Map compound_UAP = new LinkedHashMap(); + + while(true) + { + int fspec = data[index+idx] & 0xff; + if(!makeUAP_Compound(compound_UAP, idx, fspec, di_frn)) break; + ++idx; + } + + index += (idx+1); + + Iterator it = compound_UAP.keySet().iterator(); + + while(it.hasNext()) + { + int idx_after = index; + + Integer frn = it.next(); + AsterixSpecVO_DataItem _di = compound_UAP.get(frn); + index = makeResult( _di, data, index); + + printDebugLog(data, idx_after, index, _di); + + if(index == -1) break; + } + + + } + catch(Exception e) + { + e.printStackTrace(); + logger.error("Exception:" + e + "sFrn:" + sFrn + " data:[" + Arrays.toString(data) + "]"); + index = -1; + } + + return index; + } + + private int makeResult_Variable(byte[] data, int index, String sFrn) { + + try + { + String di_frn = sFrn; + + Map variable_UAP = new LinkedHashMap(); + + makeUAP_Variable(variable_UAP, di_frn, data, index); + + Iterator it = variable_UAP.keySet().iterator(); + + while(it.hasNext()) + { + int idx_after = index; + + Integer frn = it.next(); + AsterixSpecVO_DataItem _di = variable_UAP.get(frn); + index = makeResult(_di, data, index); + + printDebugLog(data, idx_after, index, _di); + + if(index == -1) break; + } + + return index; + } + catch(Exception e) + { + logger.error("Exception:" + e + "sFrn:" + sFrn + " data:[" + Arrays.toString(data) + "]"); + return -1; + } + } + + private int makeResult_Repetitive(byte[] data, int index, AsterixSpecVO_DataItem di) { + int di_len = Integer.parseInt(di.getOctet()); + + try + { + List st = di.getStructure(); + AsterixSpecVO_Structure s = st.get(0); + if(s == null) return index + di_len; + + int rep = data[index] & 0xff; + + ++index; +// Map result_map = new HashMap(); + di.setOctet(String.valueOf(di_len-1)); + di.setFormat("fixed"); + + for(int i=0; i 4) { + logger.error("[AsterixParserUtils:makeNeverSent] target.length:" + target.length ); + }else { + no = apUtils.signedToInt(target); + } + + + resultMap.put(di.getItemno(), String.valueOf(no)); + + // debug log 출력 + //printDataLog("NeverSent", di, map, target); + + return index; + } + + + /***************************************** Make UAP *****************************************/ + + private boolean makeUAP(Map mUAP, int idx, int fspec ) { + + // byte로 변환한 수신페킷중 3, 4, 5, 6 번째 byte를 이용하여 어떤 fspec이 포함되어 있는지 확인하고 순서에 맞게 분석용 UAP를 생성 + + if(mUAP == null) mUAP = new HashMap(); + if(idx < 0) return false; + +// logger.error("[AsterixParserUtils] UAP_" + idx + "_data:" + prtIntToCnv(fspec), LogLevelType.LOG_DEBUG, "AllLog"); + + // "& 0xff" : 8비트만 사용하기 위함 + fspec = fspec & 0xff; + + boolean extendable = false; + + // 1이면 FX(Field Extension Indicator) + if((fspec & 0x01) == 0x01) extendable = true; + + // 128(8bit, 0b10000000), 맨 앞자리부터 체크하기 위함 + int comp = 0x80; + + //UAP의 FX를 제외한 FRN 순번 체크 1~7, 8~14, 15~21, 22~28 (idx = FRN 그룹순번 1 ~ 4) + int offset = (idx * 7) + 1; + + // 8bit를 하나씩 체크하여 1인 것만 생성 + // offset => FRN 번호 + for(int i=0; i<7; i++) + { + if((fspec & comp) == comp) mUAP.put(new Integer(offset), apUtils.findAsterixDataItem(offset, currentSpec)); + + // 8번째자리 -> 7번째자리 -> 6번째자리 -> ... -> 1번째 자리 + comp = comp >>> 1; + ++offset; + } + + return extendable; + + } + + private boolean makeUAP_Compound(Map mUAP, int idx, int fspec, String frn) { + if(mUAP == null) mUAP = new HashMap(); + if(idx < 0) return false; + + fspec = fspec & 0xff; + + boolean extendable = false; + if((fspec & 0x01) == 0x01) extendable = true; + + int comp = 0x80; + + int offset = (idx * 7) + 1; + + for(int i=0; i<7; i++) + { + if(((fspec & comp) == comp) ) + { + String key = frn + apUtils.zeroPadding2digitAtFront(offset); + mUAP.put(new Integer(key), apUtils.findAsterixDataItem(Integer.parseInt(key), currentSpec)); + } + comp = comp >>> 1; + ++offset; + } + + return extendable; + + } + + private void makeUAP_Variable(Map mUAP, String di_frn, byte[] data, int index) { + if(mUAP == null) mUAP = new HashMap(); + + int comp = 0x01; + int offset = 1; + int idx = index; + + while(true) + { + try + { + String key = di_frn + apUtils.zeroPadding2digitAtFront(offset); + + AsterixSpecVO_DataItem di = apUtils.findAsterixDataItem(Integer.parseInt(key), currentSpec); + if(di == null) { + key = di_frn + apUtils.zeroPadding2digitAtFront(1); + di = apUtils.findAsterixDataItem(Integer.parseInt(key), currentSpec); + } + if(di == null) break; + + mUAP.put(new Integer(key), di); + + int data_length = Integer.parseInt(di.getOctet()); + idx = idx + data_length; + int next = data[idx-1] & 0xff; + + if((next & comp) != comp) break; + ++offset; + } + catch(Exception e) + { + break; + } + } + } + + + /***************************************** Service Field *****************************************/ + + public boolean filterSvcData(ServiceAsdeData _scMap) { + + if(_scMap == null) return false; + + boolean bFilter = true; + + if (!_scMap.getTrgt_id().equals("") + || !_scMap.getMode_s_cd().equals("") + || !_scMap.getSsr_cd().equals("") + || !_scMap.getClsgn().equals("") + || !_scMap.getTail_no().equals("") + || !_scMap.getTrack_no().equals("") + ) { + + if (!(_scMap.getLat().equals("0") && _scMap.getLon().equals("0"))) { + bFilter = true; + // if( _scMap.getCarty() != null){ + // bFilter = false; + // } + + } else { + bFilter = false; + } + + } else { + bFilter = false; + } + + return bFilter; + } + + + public void inputServiceField(AsterixSpecVO_Structure struc, String sValue) { + + String sServiceName = struc.getServiceFieldName(); + + fieldUtils.setItem(scMap, sServiceName, sValue.trim()); + + return; + + } + + private void printDebugLog(byte[] logdata, int sidx, int tidx, AsterixSpecVO_DataItem di) { + + if( !debugLogMode.equals("detail")) return; + + int iSize = tidx - sidx; + + if(iSize <= 0) return ; + + // log + byte[] bLogByte = new byte[tidx - sidx]; + System.arraycopy(logdata, sidx, bLogByte, 0, tidx - sidx); + + String sLotTxt = ""; + if(di.getStructure() == null) return; + + sLotTxt = di.getItemno().concat(" : ") + .concat(apUtils.prtStrucItems(di.getStructure(), resultMap)) + .concat(" ").concat(apUtils.cnvBytesToHex(bLogByte)) + .concat(" ").concat(Arrays.toString(bLogByte)) ; + + + logger.debug("[AsterixParser:makeResult] parser_Log [" + sLotTxt + "]"); + } + + public void setDebugLogMode(String debugLogMode) { + this.debugLogMode = debugLogMode; + } + + + +} diff --git a/src/main/java/kr/gmtc/gw/asderecv/asde/parser/AsterixParserThread.java b/src/main/java/kr/gmtc/gw/asderecv/asde/parser/AsterixParserThread.java new file mode 100644 index 0000000..7ef0de6 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/asde/parser/AsterixParserThread.java @@ -0,0 +1,133 @@ +package kr.gmtc.gw.asderecv.asde.parser; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Queue; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.fasterxml.jackson.databind.ObjectMapper; + +import kr.gmtc.gw.asderecv.rest.vo.ServiceAsdeData; + +public class AsterixParserThread { + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + private AsterixParser asterixParser; + + private boolean isRunning = true; + private Thread thread; + + private Integer scvQcount = 3; + private String debugLogMode = "none"; + + List serviceMap; + + ObjectMapper mapper; + + DateTimeFormatter dfPattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + + @SuppressWarnings("unused") + public AsterixParserThread(Queue packetQ, HashMap> serviceQueue) { + thread = new Thread(new Runnable() { + @Override + public void run() { + while (isRunning) { + String packetMsg = ""; + try { + packetMsg = packetQ.poll(); + + if (packetMsg != null) { + + // 테스트용 + //String sBynary = "0A 05 BB E3 81 F1 91 03 8A 06 04 98 3C 00 C1 D9 F5 28 0E 72 45 54 48 33 36 36 37 20 04 01 68 45 54 41 57 45 20 20 20 02 8D 36 34 39 20 00 33 34 52 0B 8F 08 EB 81 71 01 03 02 06 04 98 41 00 D3 95 20 50 08 06 E8 41 46 4C 36 20 20 20 20 71 D1 48 53 49 41 31 34 38 20 20 08 20 EB 81 01 01 03 02 06 04 98 3D C1 CF 75 18 D7 01 00 E0 02 E0 EB 81 71 01 03 02 06 04 98 40 C1 EA EC E5 80 00 00 E8 46 49 52 45 35 20 20 20 71 D1 08 53 49 41 31 30 38 20 20 08 20 EB 81 01 01 03 02 06 04 98 3C 45 E9 6B EC FB 00 00 E0 02 E0 E3 81 81 81 03 02 06 04 98 3F C5 A3 C8 F1 38 06 03 02 AC 00 20 E3 81 81 81 03 02 06 04 98 41 05 46 90 F1 38 06 02 02 6A 00 20 EB 81 F1 91 03 8A 06 04 98 3E 46 E3 86 26 F7 F9 FB F2 07 E3 54 57 42 32 39 32 20 20 71 C3 24 48 4C 38 33 32 34 20 20 3F FE 31 32 39 20 02 33 33 52 04 A7 08 EB 81 F1 11 03 0A 06 04 98 3F 86 D5 73 10 DC FF 00 F8 04 00 4B 41 53 38 20 20 20 20 71 C2 12 48 4C 38 32 31 32 20 20 35 34 36 20 02 04 9A 08 EB 81 01 01 03 02 06 04 98 40 07 CB 78 28 01 00 00 E0 02 E0 EB 81 01 01 03 02 06 04 98 3C CB D6 2D 0F 8A 00 00 E0 02 E0 EB 81 01 01 03 02 06 04 98 3B 8E D3 69 14 D5 00 00 E0 02 E0 EB 81 01 01 03 02 06 04 98 30 51 E8 81 14 CC 00 00 E0 00 E0 EB 81 F1 91 03 8A 06 04 98 3E 12 DA FA 58 54 A1 07 F5 20 08 CF 4B 41 4C 35 35 31 20 20 71 BE 01 48 4C 37 36 30 31 20 20 00 6C 36 30 39 20 00 33 34 52 06 A5 08 E3 81 F1 91 03 8A 06 04 98 3C 14 E4 E6 F5 28 0E 73 41 49 48 32 32 31 20 20 71 C5 03 48 4C 38 35 30 33 20 20 02 73 36 31 36 20 00 33 34 52 05 B4 08 EB 81 F1 91 03 8A 06 04 98 3E 14 D1 26 0E 46 03 02 F2 09 1B 43 53 4E 33 30 36 31 20 78 1D AC 42 33 32 39 53 20 20 20 3F FE 31 32 34 20 02 33 34 4C 04 A6 08 EB 81 01 01 03 02 06 04 98 42 D8 CC 92 18 B2 00 00 E0 00 E0 EB 81 01 01 03 02 06 04 98 35 98 DE 2A 14 0B 00 00 E0 02 E0 EB 81 01 01 03 02 06 04 98 3E DA 0B 2E 16 DE FF 00 E0 00 E0 EB 81 01 01 03 02 06 04 98 40 9A DE FC 21 72 00 00 E0 00 E0 EB 81 01 01 03 02 06 04 98 27 5C D3 D8 32 98 00 00 E0 00 E0 EB 81 F1 91 03 8A 06 04 98 3E 9C D4 72 FA 1A E4 27 F2 07 A8 4B 41 4C 37 35 36 20 20 71 C2 76 48 4C 38 32 37 36 20 20 3F FD 32 34 36 20 02 33 34 4C 04 A8 08 EB 81 01 01 03 02 06 04 98 3F A0 EC 22 01 A7 00 00 E0 02 E0 EB 81 F1 91 83 8A 06 04 98 3E A1 2A 7C CF 65 D6 3B F2 06 BD 4A 4E 41 32 38 32 20 20 71 BF 57 48 4C 37 37 35 37 20 20 00 1D 32 30 20 20 80 71 33 33 52 00 33 33 52 04 A9 08 EB 81 F1 91 03 8A 06 04 98 40 22 05 38 14 50 FC FD F4 0E 70 47 45 43 38 33 39 31 20 3C 70 C9 44 41 4C 46 49 20 20 20 3F FF 36 33 33 20 02 33 34 52 04 23 08 EB 81 01 01 03 02 06 04 98 3F E4 EF 9C 04 0B 00 00 E0 00 E0 EB 81 71 01 03 02 06 04 98 3C E5 D9 E1 09 F9 FD 09 E8 53 41 46 45 54 59 31 20 71 D1 13 53 49 41 31 31 33 20 20 0A 20 EB 81 01 01 03 02 06 04 98 3F E5 EE CB FA B5 00 00 E0 00 E0 EB 81 F1 91 03 8A 06 04 98 3D 66 FE 13 13 E4 08 F5 F2 08 F2 43 59 5A 32 33 31 41 20 78 05 CA 42 32 38 38 31 20 20 20 3F FE 36 30 34 20 02 33 33 52 04 A4 08 EB 81 F1 91 03 8A 06 04 98 3F 66 F8 39 07 39 FB FD F4 0E 63 46 44 58 35 33 39 31 20 AC 25 C4 4E 38 38 32 46 44 20 20 3F FE 36 35 32 20 02 33 34 52 08 97 08 EB 81 E1 81 03 02 06 04 98 3B 28 10 A1 72 50 C4 44 F1 10 00 C1 48 4C 35 32 33 39 20 20 71 AA 39 01 6C 00 20 E3 81 F1 91 03 8A 06 04 98 3E 68 86 EA F5 20 0E 5D 44 4C 48 37 31 39 20 20 3C 67 0E 44 41 49 58 4E 20 20 20 00 6B 33 32 20 20 00 33 34 52 04 24 08 EB 81 01 01 03 02 06 04 98 41 29 CA 2D 1A CC 00 00 E0 02 E0 EB 81 01 01 03 02 06 04 98 39 2A 07 DB 16 5D 00 00 E0 00 E0 EB 81 01 01 03 02 06 04 98 3D ED 0D E0 18 E4 00 00 E0 00 E0 EB 81 01 01 03 02 06 04 98 2A AF CB D2 26 53 00 00 E0 02 E0 EB 81 F1 11 03 02 06 04 98 3A EF 0D 76 04 91 00 00 F2 08 E4 42 33 32 37 37 20 20 20 78 11 D5 42 33 32 37 37 20 20 20 38 34 32 20 02 E0 EB 81 01 01 03 02 06 04 98 3C 2F EA 89 ED 0F 00 00 E0 02 E0 EB 81 01 01 03 02 06 04 98 3B F2 CA 19 24 BF 03 03 E0 02 E0 EB 81 01 01 03 02 06 04 98 41 34 0F 9F 1B C5 00 00 E0 00 E0 EB 81 71 01 03 02 06 04 98 3D B7 01 C9 F6 FB FF FF E8 46 49 52 45 33 20 20 20 71 D1 09 53 49 41 31 30 39 20 20 08 20 EB 81 01 01 03 02 06 04 98 3D F8 D4 C1 12 10 00 00 E0 02 E0 E3 81 81 81 03 02 06 04 98 40 39 4E BC F1 38 06 01 02 44 00 20 EB 81 F1 91 03 8A 06 04 98 3D BA DA EC 01 57 05 F8 F2 06 A6 41 41 52 31 31 31 20 20 71 BF 92 48 4C 37 37 39 32 20 20 3F FE 32 31 20 20 02 33 34 4C 04 A2 08 EB 81 E1 81 03 02 06 04 98 3D BC 13 53 7B 69 C7 58 F1 30 0F 14 4A 4E 41 33 38 30 20 20 71 C0 14 02 29 00 20 EB 81 01 01 03 02 06 04 98 3D 7F 11 2A 1B 1E 00 00 E0 00 E0 "; + //sBynary = sBynary.replaceAll("\\p{Z}", ""); + + // UDP 수신데이터 + String sBynary = packetMsg.replaceAll("\\p{Z}", ""); + String sTime = null; + + // 수신시각 + if(sBynary.indexOf("*") > 0) { + sTime = packetMsg.substring(packetMsg.indexOf("*") +1); + sBynary = sBynary.substring(0, sBynary.indexOf("*")); + }else { + sTime = LocalDateTime.now().format(dfPattern); + } + + // 수신 메시지(Hex) byte로 변환 + byte[] data = new java.math.BigInteger(sBynary, 16).toByteArray(); + + // byte문자열 확인 + // int recvCnt ++; +// logger.writeLevelLog("[AsterixParserThread] ByteArray(" + recvCnt + "):" + packetMsg, LogLevelType.LOG_DEBUG, "AllLog"); + + // 수신메시지 파싱 + List> result = asterixParser.parse(data, sTime); + + // logger.info("[ASDE분석결과:"+LocalDateTime.now().format(dfPattern)+"] result = "+ result); + //logger.info("[ASDE분석결과(service):"+LocalDateTime.now().format(dfPattern)+"] serviceMap = "+ Arrays.deepToString(serviceMap.toArray())); + + if(!serviceMap.isEmpty()) { + + for(int idx=0; idx Packet Message : " + packetMsg); + } else { + logger.error("Parsing Error Packet Message :" + packetMsg); + } + + } + + } + } + }); + + } + + public void start() { + + serviceMap = new ArrayList(); + + // Asterix Parser 객체 생성 (category별로 상이한 yaml파일의 spec내용을 로드) + asterixParser = new AsterixParser(serviceMap); + asterixParser.setDebugLogMode(debugLogMode); + + mapper = new ObjectMapper(); + + isRunning = true; + thread.start(); + } + + public void stop(){ + isRunning = false; + } + + public void setScvQcount(Integer scvQcount) { + this.scvQcount = scvQcount; + } + + public void setDebugLogMode(String debugLogMode) { + this.debugLogMode = debugLogMode; + } + + + + +} diff --git a/src/main/java/kr/gmtc/gw/asderecv/asde/utils/AsterixParserUtils.java b/src/main/java/kr/gmtc/gw/asderecv/asde/utils/AsterixParserUtils.java new file mode 100644 index 0000000..47c1beb --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/asde/utils/AsterixParserUtils.java @@ -0,0 +1,571 @@ +package kr.gmtc.gw.asderecv.asde.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import kr.gmtc.gw.asderecv.asde.asterixSpec.vo.AsterixSpecVO; +import kr.gmtc.gw.asderecv.asde.asterixSpec.vo.AsterixSpecVO_DataItem; +import kr.gmtc.gw.asderecv.asde.asterixSpec.vo.AsterixSpecVO_Structure; + +import java.util.*; + +public class AsterixParserUtils +{ + private Logger logger = LoggerFactory.getLogger(AsterixParserUtils.class); + + private double AVG_ERAD_NM = 6371.0 / 1.852; + private double DE2RA = 0.017453292519943295; + private double RA2DE = 57.295779513082323; + private double GPI = 3.14159265359; + + public double CONST_BASE_LAT = 37.454228; + public double CONST_BASE_LON = 126.460915; + + ///////////////////**************************////////////////////////// + + public AsterixSpecVO_DataItem findAsterixDataItem(int idx, AsterixSpecVO spec) + { + try + { + List di = spec.getDataitem(); + if(di == null) return null; + String idxString = String.valueOf(idx); + + for(int i=0; i < di.size(); i++) + { + AsterixSpecVO_DataItem d = di.get(i); + if(d.getFrn().equals(idxString)) + { + return d; + } + } + } + catch(Exception e) + { + logger.error("Exception:" + e); + } + + return null; + } + + public String byteToString(int b) + { + return String.format("0x%02x", b&0xff); + } + + public String byteArrayToString(byte[] b) + { + StringBuilder sb = new StringBuilder(); + sb.append("0x"); + for(int i=0; i 4) { + throw new RuntimeException("Too big to convert to integer"); + } + + for(int i=0; i 8) { + throw new RuntimeException("Too big to convert to integer"); + } + + for(int i=0; i 4) { + return 0; + } + + for(int i=0; i msb) return octetSeq; + } + + return -1; + } + + public String makeOctal(byte[] buf) + { + if(buf == null || buf.length != 2) return ""; + + int num = buf[0] & 0xff; + int num2 = buf[1]; + int n = buf[0] & 0x0f; + n = n >>> 1; + + StringBuffer sbuffer = new StringBuffer(); + sbuffer = makeString(sbuffer, n); + + n = ((num2 >>> 6) & 0x03) | ((num & 0x01) << 2); + + sbuffer = makeString(sbuffer, n); + + n = ((num2 >>> 3) & 0x07); + sbuffer = makeString(sbuffer, n); + + n = num2 & 0x07; + sbuffer = makeString(sbuffer, n); + + return sbuffer.toString(); + } + + public String makeICAOCode(byte[] buf) + { + if(buf == null || buf.length != 6) return ""; + + StringBuffer sb = new StringBuffer(); + + int octet1 = buf[0]; + octet1 = octet1 & 0xff; + + octet1 = octet1 >>> 2; + sb = makeTargetIdentification(sb, octet1); + + octet1 = buf[0]; + octet1 = octet1 & 0xff; + int octet2 = buf[1]; + octet2 = octet2 & 0xff; + octet2 = octet2 >>> 4; + + octet1 = octet1 << 4; + octet1 = octet1 & 0x30; + int number = octet1 | octet2; + sb = makeTargetIdentification(sb, number); + + octet1 = buf[1] & 0xff; + octet2 = buf[2] & 0xff; + + octet1 = octet1 << 2; + octet1 = octet1 & 0x3f; + octet2 = octet2 >>> 6; + + number = octet1 | octet2; + sb = makeTargetIdentification(sb, number); + + octet1 = buf[2]; + number = octet1 & 0x3f; + sb = makeTargetIdentification(sb, number); + + octet1 = buf[3]; + octet1 = octet1 & 0xff; + number = octet1 >>> 2; + sb = makeTargetIdentification(sb, number); + + octet1 = buf[3] & 0xff; + octet2 = buf[4] & 0xff; + + octet1 = octet1 << 4; + octet1 = octet1 & 0x3f; + octet2 = octet2 >>> 4; + + number = octet1 | octet2; + sb = makeTargetIdentification(sb, number); + + octet1 = buf[4] & 0xff; + octet2 = buf[5] & 0xff; + + octet1 = octet1 << 2; + octet1 = octet1 & 0x3f; + octet2 = octet2 >>> 6; + + number = octet1 | octet2; + sb = makeTargetIdentification(sb, number); + + octet1 = buf[5]; + number = octet1 & 0x3f; + sb = makeTargetIdentification(sb, number); + + return sb.toString(); + } + + public String makeAscii(byte[] buf) + { + String retVal = ""; + + if(buf == null || buf.length == 0) return ""; + + retVal = new String(buf); + + return retVal.trim(); + } + + public String zeroPadding2digitAtFront(int number) + { + if(number <= 0) return "01"; + try + { + String str = String.valueOf(number); + int length = str.length(); + + if(length >= 2) return str; + else return "0" + str; + } + catch(Exception e) + { + logger.error("Exception:" + e); + return "01"; + } + } + + private StringBuffer makeTargetIdentification(StringBuffer sb, int val) + { + if(val == 0) return sb; + else if(val == 32) + { + sb.append(" "); + return sb; + } + else if(val >= 1 && val <= 26) + { + sb.append(Character.toChars(64 + val)); + return sb; + } + else if(val >= 48 && val <= 57) + { + sb.append(Character.toChars(val)); + return sb; + } + else return sb; + + } + +///////////////////**************************////////////////////////// + + + + public String prtbyteToCnv(byte[] byteData, int flag) { + String retStr = ""; + + String toHex = ""; + + String toByte = Arrays.toString(byteData); + + String toBynary = ""; + + String to8bitDec = ""; + + for(int idx=0; idx < byteData.length; idx++) + { + if(!toHex.equals("")) { + toHex = toHex.concat(" "); + toBynary = toBynary.concat(" "); + to8bitDec = to8bitDec.concat(" "); + } + + toHex = toHex.concat(String.format("%02X", byteData[idx] &0xff)); + + toBynary = toBynary.concat(String.format("%08d", Integer.parseInt(Integer.toBinaryString(byteData[idx] &0xff )))); + + to8bitDec = to8bitDec.concat(Integer.toString(byteData[idx] & 0xff)); + + } + + if(flag == 0) + retStr = "DATA: " + toHex + " " + toBynary + " " + toByte + " " + to8bitDec; + else + retStr = "DATA: " + toHex; + + return retStr; + } + + public double calcAngleDegree(double y1, double x1, double y2, double x2) { + + double angle; + double dx; + double dy; + + dx = x2 - x1; + dy = y2 - y1; + + // 두점사이의 절대각 + angle = Math.atan2(dy, dx); + + // ArcTan2 함수의 결과값은 라디안이므로, 도 단위로 변환 + angle = angle * 180 / Math.PI; + + if (angle < 0) + angle = 360 + angle; + + return angle; + + } + + public double calcDirection(double Vy, double Vx) { + + double angle; + + // 두점사이의 절대각 + angle = Math.atan2(Vy, Vx); + + // ArcTan2 함수의 결과값은 라디안이므로, 도 단위로 변환 +// angle = angle * 180 / Math.PI; +// +// //if (angle > 160) + angle = Math.toDegrees(angle) ; + + if (angle < 0) + angle = 360 + angle; + + return angle; + + } + + public Map calcDistanceAzimuth(double Lat, double Lon, double DistNM, double Azimuth) { + + double dA; + double dB; + double dSinb; + double dCosb; + double dSinc; + double dCosc; + double dAzrad; + double dArcCosValue; + double dArcSinValue; + + Map retMap = new HashMap(); + + dB = DistNM / AVG_ERAD_NM; + dSinb = Math.sin(dB); + dCosb = Math.cos(dB); + dSinc = Math.sin(DE2RA * (90.0 - Lat)); + dCosc = Math.cos(DE2RA * (90.0 - Lat)); + dAzrad = DE2RA * Azimuth; + + dArcCosValue = dCosb * dCosc + dSinc * dSinb * Math.cos(dAzrad); + if(dArcCosValue < -1) dArcCosValue = -1; + if(dArcCosValue > 1) dArcCosValue = 1; + dA = Math.acos(dArcCosValue); + + dArcSinValue = dSinb * Math.sin(dAzrad) / Math.sin(dA); + if(dArcSinValue < -1) dArcSinValue = -1; + if(dArcSinValue > 1) dArcSinValue = 1; + dB = Math.asin(dArcSinValue); + + double outLat = RA2DE * ((GPI/2.0) - dA); + double outLon = RA2DE * dB + Lon; + + retMap.put("lat", outLat); + retMap.put("lon", outLon); + + return retMap; + } + + public int parseIntDef(String sValue) { + + int defaultValue = 0; + + if (sValue == null) { + return defaultValue; + } else { + try { + double dVal = Double.parseDouble(sValue); + return (int) dVal; + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + } + + public double parseDoubleDef(String sValue) { + + double defaultValue = 0; + + if (sValue == null) { + return defaultValue; + } else { + try { + return Double.parseDouble(sValue); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + } + + public String prtIntToCnv(int byteData) { + + String retStr = ""; + + String toHex = String.format("%02X", byteData &0xff); + + String toByte = Integer.toString(byteData); + + String toBynary = String.format("%08d", Integer.parseInt(Integer.toBinaryString(byteData & 0xff ))); + + + retStr = "DATA: " + toHex + " " + toBynary + " " + toByte + " " + (byteData & 0xff); + + return retStr; + } + + + public void printDataLog(String pos, AsterixSpecVO_DataItem di, Map map, byte[] data) { + + //logger.writeLevelLog("[parse] [" + di.getItemno() +"] = "+map.get("SPF") + " | " + prtIntToCnv(iSPF) , LogLevelType.LOG_DEBUG, "AllLog"); + + String sRetVal = ""; + String mapName = "", mapData = ""; + String sHexData = ""; + + if(di == null ) return ; + + List struc = di.getStructure(); + + + for(int idx = 0; idx Parse: "+sRetVal, LogLevelType.LOG_DEBUG, "AllLog"); + + } + + public String cnvBytesToHex(byte[] bArr) { + + String sRetVal = ""; + int iLen = bArr.length; + + for(int idx = 0; idx st, LinkedHashMap resMap) { + +// Iterator it = st.keySet().iterator(); + String sRet = ""; + String sVal = ""; + + for(int i=0; i T yamlLoadToVo(String sFile, Class cVO) { + + if(sFile == null || sFile.equals("")) return cVO.cast(returnObj); + + try { + + InputStream is = this.getClass().getResourceAsStream("/" + sFile); + Reader reader = (is != null ? new InputStreamReader(is) : new FileReader(sFile)); + + // yaml 파일 로드 + //LinkedHashMap propMap = new Yaml().load(new FileReader(ResourceUtils.getFile(sFile))); + LinkedHashMap propMap = new Yaml().load(reader); + + // 파일내용을 JSON 포멧으로 변환 + JSONObject jsonObject = new JSONObject(propMap); + + // JSON 데이터를 Object로 변환 + ObjectMapper objectMapper = new ObjectMapper(); + + // Array Null값, 중간중간 존재하지 않는 필드 무시 + objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT); + objectMapper.configure(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY, false); + + returnObj = objectMapper.readValue(jsonObject.toJSONString(), cVO); + + } catch (Exception e) { + e.printStackTrace(); + } + + return cVO.cast(returnObj); + } + +} diff --git a/src/main/java/kr/gmtc/gw/asderecv/config/ServiceConfig.java b/src/main/java/kr/gmtc/gw/asderecv/config/ServiceConfig.java new file mode 100644 index 0000000..e4cad9d --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/config/ServiceConfig.java @@ -0,0 +1,36 @@ +package kr.gmtc.gw.asderecv.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import kr.gmtc.gw.asderecv.rest.vo.ServiceAsdeData; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Queue; +import java.util.concurrent.LinkedBlockingQueue; + +@Configuration("ServiceConfig") +public class ServiceConfig { + + @Value("${root}") + private String root; + + @Bean(name = "packetQ") + public Queue packetQ(){ + return new LinkedBlockingQueue(); + } + + @Bean(name = "serviceQueue") + public HashMap> serviceQueue(){ + return new LinkedHashMap>(); + } + + @Bean(name = "serviceRunnig") + public boolean serviceRunnig(){ + return false; + } + + +} diff --git a/src/main/java/kr/gmtc/gw/asderecv/controller/MainController.java b/src/main/java/kr/gmtc/gw/asderecv/controller/MainController.java new file mode 100644 index 0000000..830746b --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/controller/MainController.java @@ -0,0 +1,182 @@ +package kr.gmtc.gw.asderecv.controller; + +import kr.gmt.so.state.StateManager; +import kr.gmt.so.state.callback.IState; +import kr.gmtc.gw.asderecv.asde.parser.AsterixParserThread; +import kr.gmtc.gw.asderecv.rest.ServiceQueManager; +import kr.gmtc.gw.asderecv.rest.vo.ServiceAsdeData; +import kr.gmtc.gw.asderecv.utils.CmmnUtil; +import kr.gmtc.gw.comp.socket.udp.UDPEventListener; +import kr.gmtc.gw.comp.socket.udp.UDPManager; +import kr.gmtc.gw.comp.socket.utils.SocketUtil; +import kr.gmtc.gw.comp.socket.vo.MsgObjVO; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.Queue; + +import javax.annotation.Resource; + +@Component("controller") +public class MainController implements ApplicationListener{ + + Queue packetQ; + HashMap> serviceQueue; + + @Value("${root}") + String path; + + @Value("${asde.service.queueCount}") + private Integer scvQcount; + + @Value("${asde.service.clearQ.maxCount}") + private Integer scvQmaxCount; + + @Value("${asde.service.clearQ.diffTime}") + private Integer scvQdiffTime; + + @Value("${asde.service.clearQ.clearTime}") + private Integer scvQclearTime; + + @Value("${dev.debug.debugLogMode}") + private String debugLogMode = "none"; + + @Resource(name = "serviceRunnig") + private boolean serviceRunnig = false; + + protected UDPManager udpManager; + private UDPEventListener udpListener; + + private AsterixParserThread asterixParserThread; + + private ServiceQueManager serviceQueManager; + + protected Logger logger; + + @Autowired + private StateManager stateMgr; + + protected MainController(Queue packetQ, HashMap> serviceQueue){ + + this.packetQ = packetQ; + this.serviceQueue = serviceQueue; + + logger = LoggerFactory.getLogger(this.getClass()); + } + + @Order(0) + @EventListener(ApplicationReadyEvent.class) + public void initialize() { + + udpListener = createUDPEventListener(); + udpManager = new UDPManager(path, udpListener); + + asterixParserThread = new AsterixParserThread(packetQ, serviceQueue); + asterixParserThread.setScvQcount(scvQcount); + asterixParserThread.setDebugLogMode(debugLogMode); + + serviceQueManager = new ServiceQueManager(serviceQueue, serviceRunnig); + serviceQueManager.setScvQclearTime(scvQclearTime); + serviceQueManager.setScvQcount(scvQcount); + serviceQueManager.setScvQdiffTime(scvQdiffTime); + serviceQueManager.setScvQmaxCount(scvQmaxCount); + + } + + @Order(1) + @EventListener(ApplicationReadyEvent.class) + public void start() { + + udpManager.startClient(); + serviceQueManager.start(); + asterixParserThread.start(); + } + + + public void stop() {} + + ////////////////////////////////////////////////////////////////////// + + protected UDPEventListener createUDPEventListener() { + UDPEventListener listener = new UDPEventListener() { + + @Override + public void udpDataIn(MsgObjVO vo) { + recvPacketAdd(vo); + stateMgr.updateState(); + } + + @Override + public void udpSendData(MsgObjVO vo) { + throw new UnsupportedOperationException("Unimplemented method 'udpSendData'"); + } + + @Override + public void udpSendData(String ip, int port, MsgObjVO vo) { + throw new UnsupportedOperationException("Unimplemented method 'udpSendData'"); + } + + @Override + public void connected(String ip, int port, int statusCode, String description) { + throw new UnsupportedOperationException("Unimplemented method 'connected'"); + } + + @Override + public void disconnected(String ip, int port, int statusCode, String description) { + throw new UnsupportedOperationException("Unimplemented method 'disconnected'"); + } + + @Override + public void connectionStatus(String ip, int port, String connEvent, int statusCode, String description) { + throw new UnsupportedOperationException("Unimplemented method 'connectionStatus'"); + } + + }; + return listener; + } + + protected void recvPacketAdd(MsgObjVO vo) { + + if (vo.getMsg().length < 1) { + return; + } + + try { + + String msg = SocketUtil.byteArrayToHexString(vo.getMsg()); + + String recvDate = vo.getRecvDT().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")); + + msg = msg.concat("*").concat(recvDate); + + packetQ.add(msg); + + //logger.debug("[UDPDataIn] : " + msg); + + } catch (Exception e) { + + logger.error("[MainController] recvPacketAdd Exception " + CmmnUtil.getStatckTrace(e) ); + + } + } + + @Override + public void onApplicationEvent(ContextClosedEvent contextClosedEvent) { + this.stop(); + logger.info("====================== SYSTEM STOPED ======================"); + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +} diff --git a/src/main/java/kr/gmtc/gw/asderecv/logger/CustomAppender.java b/src/main/java/kr/gmtc/gw/asderecv/logger/CustomAppender.java new file mode 100644 index 0000000..6bf76e4 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/logger/CustomAppender.java @@ -0,0 +1,55 @@ +package kr.gmtc.gw.asderecv.logger; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import ch.qos.logback.core.Layout; + +public class CustomAppender extends AppenderBase { + + static int DEFAULT_LIMIT = 10; + int counter = 0; + int limit = DEFAULT_LIMIT; + + Layout layout; + + public void setLimit(int limit) { + this.limit = limit; + } + + public int getLimit() { + return limit; + } + + @Override + public void start() { + if (this.layout == null) { + addError("No layout set for the appender named [" + name + "]."); + return; + } + + String header = layout.getFileHeader(); + System.out.print(header); + super.start(); + } + + public void append(ILoggingEvent event) { + if (counter >= limit) { + return; + } + // output the events as formatted by patternLayout + String eventStr = this.layout.doLayout(event); + + System.out.print(eventStr); + + // prepare for next event + counter++; + } + + public Layout getLayout() { + return layout; + } + + public void setLayout(Layout layout) { + this.layout = layout; + } +} \ No newline at end of file diff --git a/src/main/java/kr/gmtc/gw/asderecv/rest/AsdeServiceController.java b/src/main/java/kr/gmtc/gw/asderecv/rest/AsdeServiceController.java new file mode 100644 index 0000000..8b6e429 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/rest/AsdeServiceController.java @@ -0,0 +1,297 @@ +package kr.gmtc.gw.asderecv.rest; + +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.LinkedBlockingQueue; + +import javax.annotation.Resource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +// import com.google.common.util.concurrent.RateLimiter; + +import kr.gmtc.gw.asderecv.rest.vo.SacpServiceVO; +import kr.gmtc.gw.asderecv.rest.vo.ServiceAsdeData; +import kr.gmtc.gw.asderecv.utils.CmmnUtil; +import kr.gmtc.gw.asderecv.rest.vo.SacpServiceHeader; + + +@RestController +public class AsdeServiceController{ + + // 프레임 워크 구성요소 // + /** 서비스 설정, {@code application.yml} */ + // @Resource(name = "ServiceConfig") + // private ServiceConfig serviceConfig; + + /* UDP test 소켓 */ + //UDPEchoClient UDPSocket ; + + @Resource(name = "serviceQueue") + HashMap> serviceQueue; + + @Resource(name = "packetQ") + Queue packetQ; + + + Queue testMap = new LinkedBlockingQueue(); + + /* 서비스중인지 확인 */ + @Resource(name = "serviceRunnig") + private boolean serviceRunnig = false; + + @Value("${asde.service.queueCount}") + private Integer scvQcount; + + @Value("${asde.service.serviceCount}") + private Integer scvDatacount; + + private SacpServiceVO asdeServiceVO; + + protected Logger logger; + + protected ObjectMapper mapper; + + DateTimeFormatter dfPattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + + public AsdeServiceController() { + + logger = LoggerFactory.getLogger(this.getClass()); + mapper = new ObjectMapper(); + } + + @GetMapping(value = "/getASDE/{qid}", produces = MediaType.APPLICATION_JSON_VALUE) + public synchronized ResponseEntity getASDE( @PathVariable Integer qid) { + + int qidx = qid -1; + + String sRetJsonData = ""; + + // synchronized(serviceQueue.get(qidx)){ + sRetJsonData = makeServiceData(qidx, serviceQueue.get(qidx)); + // } + + return ResponseEntity.ok(sRetJsonData); + + } + + @GetMapping(value = "/getQueCount/{qid}", produces = MediaType.APPLICATION_JSON_VALUE) + public synchronized ResponseEntity getQueCount( @PathVariable Integer qid) { + + int qidx = qid -1; + + String sRetJsonData = "Que Count : "; + + if(qidx == -1){ + sRetJsonData = sRetJsonData + "[" ; + + for(int idx =0;idx getASDEFilter( @PathVariable Integer qid, @PathVariable String clsgn) { + + int qidx = qid -1; + + String sRetJsonData = ""; + + synchronized(serviceQueue.get(qidx)){ + sRetJsonData = makeServiceDataFilter(qidx, serviceQueue.get(qidx), clsgn); + } + + return ResponseEntity.ok(sRetJsonData); + + } + + + private String makeServiceData(int queIdx, Queue procQue){ + + String sendCode, sendMsg ; + SacpServiceHeader jsonHeader = new SacpServiceHeader(); + List jsonData = new LinkedList(); + + sendCode = "200"; + sendMsg = ""; + + asdeServiceVO = new SacpServiceVO(); + + int qTotalSize = procQue.size(); + int qSize = qTotalSize; + + if(qSize > scvDatacount) qSize = scvDatacount; + + if(queIdx > scvQcount -1 || queIdx < 0) { + sendCode = "Err-01"; + sendMsg = "할당되지 않은 큐를 지정했습니다."; + logger.error(sendMsg); + }else { + //if(procQue.peek() == null) { + if(qSize <= 0) { + sendCode = "200"; + sendMsg = "전송할 데이터 없음."; + // logger.error("procQue size" + " / " + procQue.size()); + }else{ + + serviceRunnig = true; + + while (qSize > 0 ) { + + + ServiceAsdeData data = procQue.poll(); + + if( data != null){ + jsonData.add(data); + } + + qSize--; + } + + serviceRunnig = false ; + } + + } + + jsonHeader.setResult_code(sendCode); + jsonHeader.setResult_msg(sendMsg); + + asdeServiceVO.setHeader(jsonHeader); + + if (sendCode.equals("200")) { + + asdeServiceVO.setData(jsonData); + + int iJsonSize = jsonData.size(); + if(iJsonSize <= 0 ){ + iJsonSize = 0; + logger.info("Que["+(queIdx + 1)+"] service count :" + jsonData.size() + "/" + qTotalSize + "/" + "[분석중:"+packetQ.size()+"]"); + }else{ + iJsonSize = iJsonSize -1; + logger.info("Que["+(queIdx + 1)+"] service count :" + jsonData.size() + "/" + qTotalSize + "/" + "[Max-Tm:"+jsonData.get(iJsonSize).getRecptn_dt() +", Min-Tm:"+ jsonData.get(0).getRecptn_dt() + ", 분석중:"+packetQ.size()+"]"); + } + + + } + + String sRetJsonData = ""; + + try { + sRetJsonData = mapper.writeValueAsString(asdeServiceVO); + } catch (JsonProcessingException e) { + logger.error("[AsdeServiceController] makeServiceData-JsonProcessingException : " + CmmnUtil.getStatckTrace(e)); + } + + return sRetJsonData; + + } + + private String makeServiceDataFilter(int queIdx, Queue procQue, String... filter){ + + String sendCode, sendMsg ; + SacpServiceHeader jsonHeader = new SacpServiceHeader(); + List jsonData = new LinkedList(); + + sendCode = "200"; + sendMsg = ""; + + asdeServiceVO = new SacpServiceVO(); + + int qTotalSize = procQue.size(); + int qSize = qTotalSize; + + if(qSize > scvDatacount) qSize = scvDatacount; + + if(queIdx > scvQcount -1 || queIdx < 0) { + sendCode = "Err-01"; + sendMsg = "할당되지 않은 큐를 지정했습니다."; + logger.error(sendMsg); + }else { + //if(procQue.peek() == null) { + if(qSize <= 0) { + sendCode = "200"; + sendMsg = "전송할 데이터 없음."; + // logger.error("procQue size" + " / " + procQue.size()); + }else{ + + serviceRunnig = true; + boolean bFilter = false; + + while (qSize > 0 ) { + + ServiceAsdeData data = procQue.poll(); + + if( data != null){ + + if(filter[0].equals(data.getClsgn())){ + bFilter = true; + }else{bFilter = false;} + + if(bFilter) + jsonData.add(data); + } + + qSize--; + } + + serviceRunnig = false ; + } + + } + + jsonHeader.setResult_code(sendCode); + jsonHeader.setResult_msg(sendMsg); + + asdeServiceVO.setHeader(jsonHeader); + + if (sendCode.equals("200")) { + + asdeServiceVO.setData(jsonData); + + int iJsonSize = jsonData.size(); + if(iJsonSize <= 0 ){ + iJsonSize = 0; + logger.info("Que["+(queIdx + 1)+"] service count :" + iJsonSize + "/" + qTotalSize + "/" + "[분석중:"+packetQ.size()+"]"); + }else{ + iJsonSize = iJsonSize -1; + logger.info("Que["+(queIdx + 1)+"] service count :" + iJsonSize + "/" + qTotalSize + "/" + "[Max-Tm:"+jsonData.get(iJsonSize).getRecptn_dt() +", Min-Tm:"+ jsonData.get(0).getRecptn_dt() + ", 분석중:"+packetQ.size()+"]"); + } + + + } + + String sRetJsonData = ""; + + try { + sRetJsonData = mapper.writeValueAsString(asdeServiceVO); + } catch (JsonProcessingException e) { + logger.error("[AsdeServiceController] makeServiceData-JsonProcessingException : " + CmmnUtil.getStatckTrace(e)); + } + + return sRetJsonData; + + } + + + + +} + + diff --git a/src/main/java/kr/gmtc/gw/asderecv/rest/ServiceQueManager.java b/src/main/java/kr/gmtc/gw/asderecv/rest/ServiceQueManager.java new file mode 100644 index 0000000..d278f29 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/rest/ServiceQueManager.java @@ -0,0 +1,165 @@ +package kr.gmtc.gw.asderecv.rest; + +import java.io.IOException; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.Queue; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import kr.gmtc.gw.asderecv.rest.vo.ServiceAsdeData; +import kr.gmtc.gw.asderecv.utils.CmmnUtil; +import kr.gmtc.gw.comp.thread.CustomThread; + + +//@Component("serviceQueManager") +public class ServiceQueManager { + + HashMap> serviceQueue; + + private boolean serviceRunnig = false; + private Integer scvQcount; + private Integer scvQmaxCount; + private Integer scvQdiffTime; + private Integer scvQclearTime; + + private CustomThread serviceQClearThread; + + protected Logger logger; + + + public ServiceQueManager(HashMap> serviceQueue, boolean serviceRunnig){ + + this.serviceQueue = serviceQueue; + this.serviceRunnig = serviceRunnig; + + logger = LoggerFactory.getLogger(this.getClass()); + + // 업무 스레드 생성, 대기 없이 무한반복 + serviceQClearThread = new CustomThread("serviceQClearThread", this, CustomThread.NO_SLEEP, this::serviceQClear, null, false); + } + + public void start(){ + + // 전송용 Queue 생성 + createQueue(); + + serviceQClearThread.start(); + + } + + // 설정에따라 REST용 서비스 큐 생성 + @SuppressWarnings("unchecked") + public void createQueue() { + + // String className = "java.util.Queue"; + //Class cls = Class.forName(className); + + for(int idx=0; idx obj = null; + + try { + + obj = (Queue) java.util.LinkedList.class.newInstance(); + + } catch (InstantiationException e) { + logger.error("[ServiceQueManager] createQueue-InstantiationException : " + CmmnUtil.getStatckTrace(e)); + } catch (IllegalAccessException e) { + logger.error("[ServiceQueManager] createQueue-IllegalAccessException : " + CmmnUtil.getStatckTrace(e)); + } + + serviceQueue.put(idx, obj); + + } + + } + + private void serviceQClear() throws InterruptedException, IOException { + + String sRecvTime = ""; + + ServiceAsdeData jsonData = new ServiceAsdeData(); + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + + LocalDateTime dateTime; + + int iQcnt = 0; + + if(serviceRunnig) return; + + try { + + for(int idx=0; idx scvQmaxCount ) { + + jsonData = serviceQueue.get(idx).peek(); + + try { + sRecvTime = jsonData.getRecptn_dt(); + } catch (Exception e) { + // 비정상 큐 Clear + serviceQueue.get(idx).clear(); + } + + dateTime = LocalDateTime.parse(sRecvTime, formatter); + + Duration diff = Duration.between(dateTime, LocalDateTime.now()); + + if( scvQclearTime > 0 && diff.toMillis() > scvQclearTime) { // 일정시간 미사용으로 판단 + serviceQueue.get(idx).clear(); + logger.info("[MainController] serviceQClear : " + (idx +1) +"번 Queue 장기 미사용으로 초기화 (" + iQcnt +"건), 설정시간:" + scvQclearTime); + } + else if( diff.toMillis() > scvQdiffTime) { // 데이터 과적재 + + iQcnt = serviceQueue.get(idx).size(); + int iPollCnt = iQcnt - scvQmaxCount; + + while (iQcnt - scvQmaxCount >= 0) { + serviceQueue.get(idx).poll(); + iQcnt--; + } + + logger.info("[MainController] serviceQpoll : " + (idx +1) +"번 Queue 일부 버림 (" + iPollCnt +"건), 설정시간:" + scvQdiffTime + " 설정건수:"+ scvQmaxCount); + } + } + } + + + Thread.sleep(1000); + } catch (InterruptedException e) { + logger.error("[MainController] serviceQClear-InterruptedException : " + CmmnUtil.getStatckTrace(e)); + + } catch (Exception e) { + logger.error("[MainController] serviceQClear-Exception : " + CmmnUtil.getStatckTrace(e) + ">> " + sRecvTime.toString() ); + } + } + + public void setScvQcount(Integer scvQcount) { + this.scvQcount = scvQcount; + } + + public void setScvQmaxCount(Integer scvQmaxCount) { + this.scvQmaxCount = scvQmaxCount; + } + + public void setScvQdiffTime(Integer scvQdiffTime) { + this.scvQdiffTime = scvQdiffTime; + } + + public void setScvQclearTime(Integer scvQclearTime) { + this.scvQclearTime = scvQclearTime; + } + + + + + +} diff --git a/src/main/java/kr/gmtc/gw/asderecv/rest/vo/SacpServiceHeader.java b/src/main/java/kr/gmtc/gw/asderecv/rest/vo/SacpServiceHeader.java new file mode 100644 index 0000000..a6d831f --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/rest/vo/SacpServiceHeader.java @@ -0,0 +1,21 @@ +package kr.gmtc.gw.asderecv.rest.vo; + +public class SacpServiceHeader { + + private String result_code; // 결과코드 + private String result_msg; // 결과메시지 + + public String getResult_code() { + return result_code; + } + public void setResult_code(String result_code) { + this.result_code = result_code; + } + public String getResult_msg() { + return result_msg; + } + public void setResult_msg(String result_msg) { + this.result_msg = result_msg; + } + +} diff --git a/src/main/java/kr/gmtc/gw/asderecv/rest/vo/SacpServiceVO.java b/src/main/java/kr/gmtc/gw/asderecv/rest/vo/SacpServiceVO.java new file mode 100644 index 0000000..f154d8d --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/rest/vo/SacpServiceVO.java @@ -0,0 +1,23 @@ +package kr.gmtc.gw.asderecv.rest.vo; + +import java.util.List; + +public class SacpServiceVO { + + private SacpServiceHeader header; + private List data; + + public SacpServiceHeader getHeader() { + return header; + } + public void setHeader(SacpServiceHeader header) { + this.header = header; + } + public List getData() { + return data; + } + public void setData(List data) { + this.data = data; + } + +} diff --git a/src/main/java/kr/gmtc/gw/asderecv/rest/vo/ServiceAsdeData.java b/src/main/java/kr/gmtc/gw/asderecv/rest/vo/ServiceAsdeData.java new file mode 100644 index 0000000..f175640 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/rest/vo/ServiceAsdeData.java @@ -0,0 +1,65 @@ +package kr.gmtc.gw.asderecv.rest.vo; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ServiceAsdeData { + + private String cat_ty; // 카테고리 유형 (10:Cat10, 11:Cat11) + private String clsgn; // Callsign + private String tail_no; // 항공기등록기호 + private String mode_s_cd; // Mode-S 코드 + private String ssr_cd; // SSR 코드(squawkcode) + + private String track_no; // Track number + private String trgt_id; // 타겟 ID + private String recptn_dt; // 수신일시 + + // private String posx; //x좌표 + // private String posy; // y 좌표 + + private String lat; // 위도 + private String lon; // 경도 + private String spd; // 속도 + private String cos; // 방위 + private String trgt_ty; // 타겟 ID + + private String alt; // 고도 + // private String carty; // 조업차량 분류 + + + public ServiceAsdeData() { + super(); + this.cat_ty = ""; + this.trgt_id = ""; + this.mode_s_cd = ""; + this.ssr_cd = ""; + this.clsgn = ""; + this.tail_no = ""; + this.track_no = ""; + this.recptn_dt = ""; + // this.posx = "0"; + // this.posy = "0"; + this.lat = "0"; + this.lon = "0"; + this.spd = "0"; + this.cos = "0"; + this.trgt_ty = "0"; + this.alt = "0"; + // this.carty = ""; + } + + + + @Override + public String toString() { + return "trgt_id=" + trgt_id + ", mode_s_cd=" + mode_s_cd + ", ssr_cd=" + ssr_cd + ", clsgn=" + + clsgn + ", tail_no=" + tail_no + ", track_no=" + track_no + ", recptn_dt=" + recptn_dt + ", cat_ty=" + + cat_ty + ", lat=" + lat + ", lon=" + lon + ", spd=" + spd + ", cos=" + cos + ", trgt_type=" + trgt_ty; + } + + + +} \ No newline at end of file diff --git a/src/main/java/kr/gmtc/gw/asderecv/utils/CmmnUtil.java b/src/main/java/kr/gmtc/gw/asderecv/utils/CmmnUtil.java new file mode 100644 index 0000000..dd1f9b6 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/asderecv/utils/CmmnUtil.java @@ -0,0 +1,12 @@ +package kr.gmtc.gw.asderecv.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; + +public class CmmnUtil { + public static String getStatckTrace(Throwable t) { + StringWriter sw = new StringWriter(); + t.printStackTrace(new PrintWriter(sw)); + return sw.toString(); + } +} diff --git a/src/main/java/kr/gmtc/gw/comp/socket/event/EncryptEventListener.java b/src/main/java/kr/gmtc/gw/comp/socket/event/EncryptEventListener.java new file mode 100644 index 0000000..0058d12 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/comp/socket/event/EncryptEventListener.java @@ -0,0 +1,5 @@ +package kr.gmtc.gw.comp.socket.event; + +public abstract class EncryptEventListener implements IEncryptEventListener { + +} diff --git a/src/main/java/kr/gmtc/gw/comp/socket/event/IEncryptEventListener.java b/src/main/java/kr/gmtc/gw/comp/socket/event/IEncryptEventListener.java new file mode 100644 index 0000000..86dba57 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/comp/socket/event/IEncryptEventListener.java @@ -0,0 +1,5 @@ +package kr.gmtc.gw.comp.socket.event; + +public interface IEncryptEventListener { + public byte[] getByteArray(int id, byte[] data); +} diff --git a/src/main/java/kr/gmtc/gw/comp/socket/type/ActiveModeType.java b/src/main/java/kr/gmtc/gw/comp/socket/type/ActiveModeType.java new file mode 100644 index 0000000..3b56ef5 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/comp/socket/type/ActiveModeType.java @@ -0,0 +1,16 @@ +package kr.gmtc.gw.comp.socket.type; + +public enum ActiveModeType { + PRIMARY("PRIMARY"), + SECONDARY("SECONDARY"); + + private final String typeName; + + private ActiveModeType(String name) { + this.typeName = name; + } + + public String getName() { + return this.typeName; + } +} diff --git a/src/main/java/kr/gmtc/gw/comp/socket/type/NetStatType.java b/src/main/java/kr/gmtc/gw/comp/socket/type/NetStatType.java new file mode 100644 index 0000000..1269f46 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/comp/socket/type/NetStatType.java @@ -0,0 +1,19 @@ +package kr.gmtc.gw.comp.socket.type; + +public enum NetStatType { + NONE("NONE"), + SEND_OK("SEND_OK"), + ERROR("ERROR"), + WOULDBLOCK("WOULD_BLOCK"), + DISCONNECT("DISCONNECT"); + + private final String typeName; + + private NetStatType(String name) { + this.typeName = name; + } + + public String getname() { + return this.typeName; + } +} diff --git a/src/main/java/kr/gmtc/gw/comp/socket/udp/IUDPSocketHandler.java b/src/main/java/kr/gmtc/gw/comp/socket/udp/IUDPSocketHandler.java new file mode 100644 index 0000000..3c0b426 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/comp/socket/udp/IUDPSocketHandler.java @@ -0,0 +1,13 @@ +package kr.gmtc.gw.comp.socket.udp; + +import kr.gmtc.gw.comp.socket.vo.MsgObjVO; + +public interface IUDPSocketHandler { + public void udpDataIn(MsgObjVO vo); + public void udpSendData(MsgObjVO vo); + public void udpSendData(String ip, int port, MsgObjVO vo); + + public void connected(String ip, int port, int statusCode, String description); + public void disconnected(String ip, int port, int statusCode, String description); + public void connectionStatus(String ip, int port, String connEvent, int statusCode, String description); +} diff --git a/src/main/java/kr/gmtc/gw/comp/socket/udp/UDPEventListener.java b/src/main/java/kr/gmtc/gw/comp/socket/udp/UDPEventListener.java new file mode 100644 index 0000000..4060edd --- /dev/null +++ b/src/main/java/kr/gmtc/gw/comp/socket/udp/UDPEventListener.java @@ -0,0 +1,5 @@ +package kr.gmtc.gw.comp.socket.udp; + +public abstract class UDPEventListener implements IUDPSocketHandler { + +} diff --git a/src/main/java/kr/gmtc/gw/comp/socket/udp/UDPManager.java b/src/main/java/kr/gmtc/gw/comp/socket/udp/UDPManager.java new file mode 100644 index 0000000..1a8cb98 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/comp/socket/udp/UDPManager.java @@ -0,0 +1,342 @@ +package kr.gmtc.gw.comp.socket.udp; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import kr.gmtc.gw.comp.socket.event.EncryptEventListener; +import kr.gmtc.gw.comp.socket.type.ActiveModeType; +import kr.gmtc.gw.comp.socket.utils.SocketUtil; +import kr.gmtc.gw.comp.socket.vo.MsgObjVO; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.File; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.HashMap; + +public class UDPManager { + private String path; + private Logger logger; + + private Thread checkSocketThread; + + private HashMap udpSockets; + private UDPEventListener listener; + private boolean isRunning; + + private EncryptEventListener encryptListener; + private EncryptEventListener decryptListener; + + + public UDPManager(String path, UDPEventListener listener) { + + this.path = path; + this.listener = listener; + + this.logger = LoggerFactory.getLogger(UDPManager.class); + + this.isRunning = true; + + udpSockets = new HashMap(); + + checkSocketThread = new Thread(new Runnable() { + @Override + public void run() { + while(isRunning) { + try { + checkReconnect(); + Thread.sleep(10000); + } catch (InterruptedException e) { + logger.error(SocketUtil.getStatckTrace(e)); + } + } + } + }); + + checkSocketThread.setName("UDPManager-checkSocketThread"); + + initializeUDPFromXML(); + } + + public void start() { + isRunning = true; + logger.info("====================== SYSTEM Start ======================"); + + startClient(); + // recvQThread.start(); + // sendQThread.start(); + + } + + public void stop() { + isRunning = false; + + } + + + public void initializeUDPFromXML() { + if (path.equals("")) { + path = System.getProperty("user.dir"); + } + + File xmlFile = new File(path+"/cfg/udpsocket.xml"); + + if (!xmlFile.exists()) { + logger.error("[UDPManager] Load fail .. : "+ path+"\\cfg\\udpsocket.xml"); + return; + } + + logger.info("[UDPManager] Load file .. : "+ path+"\\cfg\\udpsocket.xml"); + + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(xmlFile); + + NodeList nodes = doc.getElementsByTagName("UdpSocket"); + + for(int i = 0; i < nodes.getLength(); i++ ) { + Node udpSocketNode = nodes.item(i); + + + int id = -1; + boolean use = false; + String primaryIP = ""; + String secondaryIP = ""; + int port = 0; + String name = ""; + boolean lineMode = true; + boolean reconnUse = true; + int reconnInterval = 60; + String inout = ""; + String mcastgroup = ""; + + for(Node child = udpSocketNode.getFirstChild(); child != null; child = child.getNextSibling()) { + if (child.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + try { + Element childElement = (Element) child; + + if(childElement.getNodeName().equals("Connection")) { + id = SocketUtil.strToIntDef(childElement.getAttribute("ID"), -1); + // tag = SocketUtil.strToIntDef(childElement.getAttribute("TAG"), -1); + primaryIP = childElement.getAttribute("PIP"); + secondaryIP = childElement.getAttribute("SIP"); + port = SocketUtil.strToIntDef(childElement.getAttribute("PORT"), 0); + use = Boolean.parseBoolean(childElement.getAttribute("USE")); + name = childElement.getAttribute("Name"); + } + else if(child.getNodeName().equals("Option")) { + lineMode = Boolean.parseBoolean(childElement.getAttribute("LineMode")); + inout = childElement.getAttribute("InOut"); + mcastgroup = childElement.getAttribute("McastGrp"); + } + else if(child.getNodeName().equals("Reconnect")) { + reconnUse = Boolean.parseBoolean(childElement.getAttribute("ReconnUSE")); + reconnInterval = SocketUtil.strToIntDef(childElement.getAttribute("Interval"), 60); + } + } catch (Exception e) { + logger.error(SocketUtil.getStatckTrace(e)); + continue; + } + } + + if (!use || primaryIP.equals("") || port == 0 || id == -1) { + continue; + } + + UDPSocket udpSocket = new UDPSocket(); + udpSocket.setId(id); + udpSocket.setName(name); + udpSocket.setPrimaryIP(primaryIP); + udpSocket.setSecodaryIP(secondaryIP); + udpSocket.setPort(port); + udpSocket.setUse(use); + udpSocket.setLineMode(lineMode); + udpSocket.setInOut(inout); + udpSocket.setMultiCastGroup(mcastgroup); + udpSocket.setReconnUse(reconnUse); + udpSocket.setInterval(reconnInterval); + udpSocket.setEventListener(listener); + udpSocket.setEncryptListener(encryptListener); + udpSocket.setDecryptListener(decryptListener); + + if (!udpSockets.containsKey(udpSocket.getId())) { + + String msg = "[UDP Socket] ID : "+ String.valueOf(udpSocket.getId()) +" IP : "+ udpSocket.getPrimaryIP() +" PORT : "+ String.valueOf(udpSocket.getPort()); + logger.info(msg); + + udpSockets.put(udpSocket.getId(), udpSocket); + } + } + } catch (Exception e) { + logger.error(SocketUtil.getStatckTrace(e)); + } + } + + public void startClient() { + for (UDPSocket udpSocket : udpSockets.values()) { + if (udpSocket == null) { + continue; + } + + if (!udpSocket.isUse()) { + continue; + } + + int isActive = udpSocket.activeSocket(udpSocket.getPrimaryIP(), udpSocket.getPort()); + String msg = ""; + if (isActive == 0) { + msg = "[UDP Manager][CONNECTED - " + udpSocket.getActiveModeName() + "]["+ udpSocket.getPrimaryIP() +"]["+ String.valueOf(udpSocket.getPort()) +"] "; + } else { + msg = "[UDP Manager][ERROR- " + udpSocket.getActiveModeName() + "]["+ udpSocket.getPrimaryIP() +"]["+ String.valueOf(udpSocket.getPort()) +"] "; + } + logger.info(msg); + } + if (checkSocketThread != null) { + checkSocketThread.start(); + } + } + + + private void checkReconnect() { + for(UDPSocket udpSocket : udpSockets.values()) { + if (udpSocket == null) { + continue; + } + + boolean isReconn = false; + LocalDateTime lastRecvDT = udpSocket.getRecvdt(); + Duration duration = Duration.between(lastRecvDT, LocalDateTime.now()); + + String logMsg = ""; + if (!udpSocket.isActive()) { + isReconn = true; + } else if(udpSocket.isReconnUse() && duration.getSeconds() >= udpSocket.getInterval()) { + isReconn = true; + logMsg = "[UDP Manager] Messages are not received for "+ String.valueOf(udpSocket.getInterval()) +" seconds"; + } + + if (isReconn) { +// String logMsg = ""; + + ActiveModeType activeSocket = udpSocket.getActiveModeType(); + String sActiveName = udpSocket.getActiveModeName(); + + if (activeSocket == ActiveModeType.PRIMARY) { + logMsg = "[UDP Manager]["+ sActiveName +"-"+ udpSocket.getSecodaryIP() +":"+ String.valueOf(udpSocket.getPort()) +"(P -> S)] "+udpSocket.getName() +" Change connected.."+ logMsg; + } else { + logMsg = "[UDP Manager]["+ sActiveName +"-"+ udpSocket.getPrimaryIP() +":"+ String.valueOf(udpSocket.getPort()) +"(S -> P)] "+udpSocket.getName() +" Change connected.."+ logMsg; + } + + logger.info(logMsg); + + int isActive = udpSocket.changeActive(); + + if (isActive == 0) { + udpSocket.setRecvdt(LocalDateTime.now()); + + logMsg = "["+ udpSocket.getCurrentIP() +":"+ String.valueOf(udpSocket.getPort()) + + "("+ String.valueOf(udpSocket.getActiveModeType()) +")] "+ + udpSocket.getName() +" Connected.. "; + } else { + logMsg = "["+ udpSocket.getCurrentIP() +":"+ String.valueOf(udpSocket.getPort()) + + "("+ String.valueOf(udpSocket.getActiveModeType()) +")] "+ + udpSocket.getName() +" Connect fail.. "; + } + logger.info(logMsg); + } + } + } + + public boolean disconnect(int id) { + boolean result = false; + + try { + UDPSocket udpSocket = this.udpSockets.get(id); + if (udpSocket != null) { + result = udpSocket.inActive(); + } + } catch(Exception e) { + logger.error("[GmtUDPManager] udp socket deactive fail : "+ SocketUtil.getStatckTrace(e)); + } + + return result; + } + + public boolean disconnect(String ip, int port) { + boolean result = false; + + try { + for(UDPSocket udpSocket : this.udpSockets.values()) { + if( (ip.equals(udpSocket.getPrimaryIP()) || ip.equals(udpSocket.getSecodaryIP())) && udpSocket.getPort() == port ) { + result = udpSocket.inActive(); + break; + } + } + } catch(Exception e) { + logger.error("[GmtUDPManager] udp socket deactive fail : "+ SocketUtil.getStatckTrace(e) ); + } + + return result; + } + + +// private void recvDataIn(MsgObjVO vo) { +// try { +// // udpDataIn(vo); + +// // if (udpListener != null) { +// // udpListener.udpDataIn(vo); +// // } + +// } catch (Exception e) { +// logger.error(SocketUtil.getStatckTrace(e)); +// } + +// } + + + + public void broadcastMsg(MsgObjVO vo) { + for (UDPSocket udpSocket : udpSockets.values()) { + if (udpSocket.equalsIP(vo.getRecvIP()) && udpSocket.getPort() == vo.getRecvPort()) { + continue; + } + MsgObjVO newVO = vo.getClone(); + + udpSocket.addSendMsg(newVO); + } + } + + public void sendMsg(String clientIP, int clientPort, MsgObjVO vo) { + for (UDPSocket udpSocket : udpSockets.values()) { + if (udpSocket.equalsIP(vo.getRecvIP()) && udpSocket.getPort() == vo.getRecvPort()) { + continue; + } + + if (udpSocket.equalsIP(clientIP) && udpSocket.getPort() == clientPort) { + MsgObjVO newVO = vo.getClone(); + + udpSocket.addSendMsg(newVO); + break; + } + } + } + + + public void setEncryptListener(EncryptEventListener encryptListener) { + this.encryptListener = encryptListener; + } + + public void setDecryptListener(EncryptEventListener decryptListener) { + this.decryptListener = decryptListener; + } + +} diff --git a/src/main/java/kr/gmtc/gw/comp/socket/udp/UDPSocket.java b/src/main/java/kr/gmtc/gw/comp/socket/udp/UDPSocket.java new file mode 100644 index 0000000..95a2bf9 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/comp/socket/udp/UDPSocket.java @@ -0,0 +1,522 @@ +package kr.gmtc.gw.comp.socket.udp; + +import java.time.LocalDateTime; +import java.util.Queue; +import java.util.concurrent.LinkedBlockingQueue; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ipworks.IPWorksException; +import ipworks.Mcast; +import ipworks.McastDataInEvent; +import ipworks.McastErrorEvent; +import ipworks.McastEventListener; +import ipworks.McastReadyToSendEvent; +import kr.gmtc.gw.comp.socket.event.EncryptEventListener; +import kr.gmtc.gw.comp.socket.type.ActiveModeType; +import kr.gmtc.gw.comp.socket.type.NetStatType; +import kr.gmtc.gw.comp.socket.utils.SocketUtil; +import kr.gmtc.gw.comp.socket.vo.MsgObjVO; +import kr.gmtc.gw.comp.socket.vo.NetStatVO; + +public class UDPSocket { + + protected Logger logger; + + private final Mcast udpport; + private final Thread sendThread; + + private int id; + private int tag; + private String name; + private String primaryIP; + private String secodaryIP; + private int port; + private ActiveModeType activeModeType; + private boolean lineMode; + private String inOut; + private String multiCastGroup; + private LocalDateTime recvdt; + private LocalDateTime senddt; + private boolean use; + private boolean reconnUse; + private int interval; + private boolean doResend; + private boolean isRunning; + + private MsgObjVO reSendVO; + private NetStatVO netStatVO; + + //private final Queue recvQ; + private final Queue sendQ; + + private EncryptEventListener encryptListener; + private EncryptEventListener decryptListener; + private UDPEventListener eventListener; + + //public UDPSocket(Queue recvQ){ + public UDPSocket(){ + + logger = LoggerFactory.getLogger(this.getClass()); + + netStatVO = new NetStatVO(); + + udpport = new Mcast(); + + id = -1; + name = ""; + primaryIP = ""; + secodaryIP = ""; + activeModeType = ActiveModeType.PRIMARY; + lineMode = true; + multiCastGroup = ""; + recvdt = LocalDateTime.now(); + senddt = LocalDateTime.now(); + isRunning = true; + reconnUse = true; + use = true; + interval = 0; + inOut = "RECV"; + //this.recvQ = recvQ; + + sendQ = new LinkedBlockingQueue(100000); + + try { + udpport.setRuntimeLicense("31504A42564131535542524131535542414E44323132323200000000000000000000000000000000505A4E384E5758350000325242435038313258424E480000"); + + udpport.addMcastEventListener(new McastEventListener() { + + @Override + public void dataIn(McastDataInEvent e) { + processRcvData(e); + } + + @Override + public void error(McastErrorEvent e) {} + + @Override + public void readyToSend(McastReadyToSendEvent e) {} + + + }); + } catch (Exception ex) { + logger.error("[UDP Socket] EventListener error : " + ex.getMessage()); + } + + sendThread = new Thread(new Runnable() { + @Override + public void run() { + while(isRunning) { + try { + udpSendMsgQue(); + Thread.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + }); + sendThread.setName("UDP-Socket-sendThread"); + sendThread.start(); + + } + + public int activeSocket(String ip, int port){ + if(udpport == null){ + logger.error("[UDP Socket] declare"); + return -1; + } + + try { + udpport.setActive(false); + + if(inOut.equals("SEND")) { + + udpport.setRemoteHost(ip); + udpport.setRemotePort(port); + udpport.setActive(true); + + }else{ + + udpport.setLocalPort(port); + udpport.setActive(true); + + if( !(multiCastGroup == null || multiCastGroup.equals("")) ) + udpport.setMulticastGroup(multiCastGroup); + + logger.info("[UDP Socket] MultiCalstGroup : " + multiCastGroup + " port:" + port); + } + + logger.info("[UDP Socket] " + inOut + " : " + ip + ":" + port); + + }catch (IPWorksException e){ + logger.error("[UDP Socket] active fail : " + e.getMessage() ); + return -1; + } + + return 0; + } + + public boolean inActive(){ + + try{ + udpport.setActive(false); + } catch(IPWorksException e) { + logger.error("[UDP Socket] inActive fail : " + e.getMessage() ); + return false; + } + + return true; + } + + public int changeActive(){ + if(udpport == null){ + logger.error("UDP socket declare"); + return -1; + } + + String ip = "" ; + + switch (activeModeType) { + case PRIMARY: + ip = this.secodaryIP; + activeModeType = ActiveModeType.SECONDARY; + break; + + case SECONDARY: + ip = this.primaryIP; + activeModeType = ActiveModeType.PRIMARY; + break; + + } + + int iActive = activeSocket(ip, port); + + return iActive; + } + + public void processRcvData(McastDataInEvent data) { + try { + recvdt = LocalDateTime.now(); + + MsgObjVO vo = new MsgObjVO(); + + vo.setMsgSeq(0); + vo.setRecvIP(udpport.getRemoteHost()); + vo.setRecvPort(udpport.getRemotePort()); + vo.setRecvID(id); + vo.setRecvTag(tag); + vo.setRecvDT(LocalDateTime.now()); + vo.setRecvServerPort(port); + vo.setMsg(data.datagram); + + recvdt = LocalDateTime.now(); + + if (decryptListener != null) { + vo.setMsg(decryptListener.getByteArray(this.id, vo.getMsg())); + } + //recvQ.offer(vo); + if (eventListener != null) { + eventListener.udpDataIn(vo); + } + + vo = null; + + } catch(Exception e) { + logger.error(SocketUtil.getStatckTrace(e)); + } + } + + private int udpSendMsgQue(){ + + int resultCode = -1; + + if (!udpport.isActive()) { + sendQ.clear(); + netStatVO.setStat(NetStatType.DISCONNECT); + return resultCode; + } + + if (sendQ == null) return -1; + + int cnt = sendQ.size(); + if(cnt > 1000){ + cnt = 1000; + } + + for(int i = 0; i <= cnt-1; i++) { + try{ + if( netStatVO.getStat() == NetStatType.NONE || netStatVO.getStat() == NetStatType.SEND_OK || !doResend){ + MsgObjVO vo = sendQ.poll(); + if (vo != null) { + resultCode = sendMsg(vo); + processSendResult(resultCode, vo, false); + } + }else{ + if (reSendVO == null) { + netStatVO.setStat(NetStatType.NONE); + resultCode = 0; + } + resultCode = sendMsg(reSendVO); + processSendResult(resultCode, reSendVO, true); + } + }catch (Exception e) { + logger.error("[UDP Socket] udpSendMsgQue error " + e.getMessage()); + } + } + return resultCode; + } + + private int sendMsg(MsgObjVO vo) { + int resultCode = -1; + if (udpport.isActive()) { + try { + + if (encryptListener != null) { + vo.setMsg(encryptListener.getByteArray(this.id, vo.getMsg())); + } + + udpport.send(vo.getMsg()); + + if (lineMode) { + udpport.send("\r\n".getBytes()); + } + + resultCode = 0; + + } catch (IPWorksException e) { + resultCode = e.getCode(); + + logger.error("[UDP Socket]["+ udpport.getRemoteHost() +"]["+ String.valueOf(udpport.getRemotePort()) +"] (" + e.getCode() + ") " + e.getMessage() ); + } + } + return resultCode; + } + + private void processSendResult(int resultCode, MsgObjVO vo, boolean isReSend) { + try { + switch(resultCode) { + + case SocketUtil.NET_SEND_OK : + netStatVO.setStat(NetStatType.SEND_OK); + netStatVO.setWouldBlockFromTime(SocketUtil.defaultTime()); + netStatVO.setWouldBlockToTime(SocketUtil.defaultTime()); + netStatVO.setDescription(""); + netStatVO.setLastSendTime(LocalDateTime.now()); + netStatVO.setRetryCount(0); + break; + + case SocketUtil.NET_WOULD_BLOCK : + if (isReSend) { + reSendVO = null; + } else { + reSendVO = vo; + } + + netStatVO.setStat(NetStatType.WOULDBLOCK); + if (netStatVO.getWouldBlockFromTime().isEqual(SocketUtil.defaultTime())) { + netStatVO.setWouldBlockFromTime(LocalDateTime.now()); + } + netStatVO.setWouldBlockToTime(LocalDateTime.now()); + netStatVO.setDescription("Operation Would Block"); + if(netStatVO.getLastSendTime().isEqual(SocketUtil.defaultTime())){ + netStatVO.setLastSendTime(LocalDateTime.now()); + } + + Thread.sleep(10); + break; + + default : + if (isReSend) { + reSendVO = null; + } else { + reSendVO = vo; + } + + netStatVO.setStat(NetStatType.DISCONNECT); + netStatVO.setWouldBlockFromTime(SocketUtil.defaultTime()); + netStatVO.setWouldBlockToTime(SocketUtil.defaultTime()); + netStatVO.setDescription("Socket Error"); + + if (netStatVO.getRetryCount() == 0) { + netStatVO.setRetryCount(netStatVO.getRetryCount() +1); + netStatVO.setLastSendTime(LocalDateTime.now()); + } + + Thread.sleep(10); + break; + } + } catch(Exception e) { + logger.error("[UDP Socket] Process send result error : " + SocketUtil.getStatckTrace(e)); + } + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public boolean isActive() { + return udpport.isActive(); + } + + public String getCurrentIP() { + return udpport.getRemoteHost(); + } + + public boolean equalsIP(String ip) { + if (ip.equals(primaryIP) || ip.equals(secodaryIP)) { + return true; + } else { + return false; + } + } + + public void addSendMsg(MsgObjVO vo) { + if (vo != null && sendQ != null && udpport.isActive()) { + sendQ.offer(vo); + } + } + + public String getActiveModeName() { + return activeModeType.getName(); + } + + + ///////////// Getter Setter //////////////////////////// + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getTag() { + return tag; + } + + public void setTag(int tag) { + this.tag = tag; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPrimaryIP() { + return primaryIP; + } + + public void setPrimaryIP(String primaryIP) { + this.primaryIP = primaryIP; + } + + public String getSecodaryIP() { + return secodaryIP; + } + + public void setSecodaryIP(String secodaryIP) { + this.secodaryIP = secodaryIP; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public ActiveModeType getActiveModeType() { + return activeModeType; + } + + public void setActiveModeType(ActiveModeType activeModeType) { + this.activeModeType = activeModeType; + } + + public boolean isLineMode() { + return lineMode; + } + + public void setLineMode(boolean lineMode) { + this.lineMode = lineMode; + } + + public String getInOut() { + return inOut; + } + + public void setInOut(String inOut) { + this.inOut = inOut; + } + + public String getMultiCastGroup() { + return multiCastGroup; + } + + public void setMultiCastGroup(String multiCastGroup) { + this.multiCastGroup = multiCastGroup; + } + + public LocalDateTime getRecvdt() { + return recvdt; + } + + public void setRecvdt(LocalDateTime recvdt) { + this.recvdt = recvdt; + } + + public LocalDateTime getSenddt() { + return senddt; + } + + public void setSenddt(LocalDateTime senddt) { + this.senddt = senddt; + } + + public boolean isUse() { + return use; + } + + public void setUse(boolean use) { + this.use = use; + } + + public boolean isReconnUse() { + return reconnUse; + } + + public void setReconnUse(boolean reconnUse) { + this.reconnUse = reconnUse; + } + + public int getInterval() { + return interval; + } + + public void setInterval(int interval) { + this.interval = interval; + } + + public void setEncryptListener(EncryptEventListener encryptListener) { + this.encryptListener = encryptListener; + } + + public void setDecryptListener(EncryptEventListener decryptListener) { + this.decryptListener = decryptListener; + } + + public void setEventListener(UDPEventListener eventListener) { + this.eventListener = eventListener; + } + + + + + +} \ No newline at end of file diff --git a/src/main/java/kr/gmtc/gw/comp/socket/utils/SocketUtil.java b/src/main/java/kr/gmtc/gw/comp/socket/utils/SocketUtil.java new file mode 100644 index 0000000..0e27300 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/comp/socket/utils/SocketUtil.java @@ -0,0 +1,722 @@ +package kr.gmtc.gw.comp.socket.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.*; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.util.*; + +public class SocketUtil { + public static final int NET_WOULD_BLOCK = 135; + public static final int NET_SEND_OK = 0; + + public static int strToIntDef(String s, int def) { + try { + + int intVal = Integer.parseInt(s); + return intVal; + + } catch (NumberFormatException e) { + return def; + } + } + + public static LocalDateTime defaultTime() { + return LocalDateTime.of(1899, 12, 30, 00, 00); + } + + public static String getStatckTrace(Throwable t) { + StringWriter sw = new StringWriter(); + t.printStackTrace(new PrintWriter(sw)); + return sw.toString(); + } + + public static String GetAddress(String addressType) { + String address = ""; + InetAddress lanIp = null; + try { + + String ipAddress = null; + Enumeration net = null; + net = NetworkInterface.getNetworkInterfaces(); + + while (net.hasMoreElements()) { + NetworkInterface element = net.nextElement(); + Enumeration addresses = element.getInetAddresses(); + + while (addresses.hasMoreElements()) { + InetAddress ip = addresses.nextElement(); + if (ip instanceof Inet4Address) { + if (ip.isSiteLocalAddress()) { + ipAddress = ip.getHostAddress(); + lanIp = InetAddress.getByName(ipAddress); + } + } + } + } + + if (lanIp == null) + return null; + + if (addressType.equals("ip")) { + address = lanIp.toString().replaceAll("^/+", ""); + } else if (addressType.equals("mac")) { + address = getMacAddress(lanIp); + } else { + throw new Exception("Specify \"ip\" or \"mac\""); + } + + } catch (UnknownHostException ex) { + ex.printStackTrace(); + } catch (SocketException ex) { + ex.printStackTrace(); + } catch (Exception ex) { + ex.printStackTrace(); + } + + return address; + } + + public static String getMacAddress(InetAddress ip) { + String address = null; + try { + NetworkInterface network = NetworkInterface.getByInetAddress(ip); + byte[] mac = network.getHardwareAddress(); + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < mac.length; i++) { + sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : "")); + } + address = sb.toString(); + } catch (SocketException ex) { + ex.printStackTrace(); + } + return address; + } + + public static String makeCRC(String value) throws Exception { + return makeCRC(value, "EUC-KR"); + } + + public static String makeCRC(String value, String charset) throws Exception { + + byte[] byteStr = value.getBytes(charset); + byte checkSum = 0; + + for (int i = 1; i < byteStr.length; i++) { + checkSum = (byte) (checkSum ^ byteStr[i]); + } + + String result = String.format("%02X", checkSum); + + return result; + } + + public static boolean checkCRC(String value, String seperator) throws Exception { + String data; + String CRCChar; + String CRCValue; + String makeStr; + + if (value.length() < 3) {return false;} + + data = value.substring(0, value.length() - 3); + CRCChar = value.substring(value.length() - 3, value.length() - 2); + CRCValue = value.substring(value.length() - 2, value.length()); + + if (!CRCChar.equals(seperator)) {return false;} + + makeStr = makeCRC(data); + + if (makeStr.equals(CRCValue)) {return true;} + + return false; + } + + + public static String fillString(int digit, String changeStr, boolean isLeft) { + String result = ""; + + result = changeStr; + + if (isLeft) { + result = String.format("%" + String.valueOf(digit) + "s", changeStr); + } + else { + result = String.format("%-" + String.valueOf(digit) + "s", changeStr); + } + result = result.substring(0, digit); + + return result; + } + + public static String byteArrayToHexString(byte[] bytes){ + + StringBuilder sb = new StringBuilder(); + + for(byte b : bytes){ + + sb.append(String.format("%02X ", b&0xff)); + } + + return sb.toString(); + } + + + /** + * int를 바이너리 스트링으로 변환하고 , length 에 부족한 길이만큼 0으로 채움 + * + * @param a + * @return + */ + public static String IntToBinaryString(int a, int length) { + String bin = Integer.toBinaryString(a); + int offset = length - bin.length(); + + if (offset < 0) { + + } else { + for (int i = 0; i < offset; i++) { + bin = "0" + bin; + } + } + + return bin; + } + + + /** + * 바이너리 스트링을 바이트로 변환 + * + * @param s + * @return + */ + public static byte binaryStringToByte(String s) { + byte ret = 0, total = 0; + for (int i = 0; i < 8; ++i) { + ret = (s.charAt(7 - i) == '1') ? (byte) (1 << i) : 0; + total = (byte) (ret | total); + } + return total; + } + + /** + * 바이너리 스트링을 바이트배열로 변환 + * + * @param s + * @return + */ + public static byte[] binaryStringToByteArray(String s) { + int count = s.length() / 8; + byte[] b = new byte[count]; + for (int i = 1; i <= count; ++i) { + String t = s.substring((i - 1) * 8, i * 8); + b[i - 1] = binaryStringToByte(t); + } + return b; + } + + /** + * 바이너리스트링을 Base64로 인코딩 + * + * @param data + * @return + */ + public static String encodeBase64(String data) { + byte[] b = binaryStringToByteArray(data); + String p = Base64.getEncoder().encodeToString(b); + + return p; + } + + + /** + * 바이너리 바이트 배열을 스트링으로 변환 + * + * @param b + * @return + */ + public static String byteArrayToBinaryString(byte[] b) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < b.length; ++i) { + sb.append(byteToBinaryString(b[i])); + } + return sb.toString(); + } + + /** + * 바이너리 바이트를 스트링으로 변환 + * + * @param n + * @return + */ + public static String byteToBinaryString(byte n) { + StringBuilder sb = new StringBuilder("00000000"); + for (int bit = 0; bit < 8; bit++) { + if (((n >> bit) & 1) > 0) { + sb.setCharAt(7 - bit, '1'); + } + } + return sb.toString(); + } + + + /** + * Base64를 바이너리스트링으로 디코딩 + * + * @param s + * @return + */ + public static String decodeBase64(String s) { + byte[] decodedBytes = Base64.getDecoder().decode(s); + String p = byteArrayToBinaryString(decodedBytes); + return p; + } + + /** + * 바이너리스트링을 Integer로 변환 + * + * @param Binary + * @return + */ + public static Integer BinaryToInteger(String Binary) { + int ResutValue = 0; + boolean chk = false; + String ComvertBinary = ""; + String StrBinary = Binary.replaceAll(" ", ""); + String[] ArrayValue = StrBinary.split(""); + + switch (ArrayValue[0]) { + case "0": + ResutValue = Integer.parseInt(StrBinary, 2); + break; + case "1": + for (int i = ArrayValue.length-1; i >= 1; i--) { + if (ArrayValue[i].equals("1") && !chk) { + ComvertBinary = "1" + ComvertBinary; + chk = true; + continue; + } + + if (chk) { + switch (ArrayValue[i]) { + case "0": + ComvertBinary = "1" + ComvertBinary; + break; + case "1": + ComvertBinary = "0" + ComvertBinary; + break; + } + } + else { + ComvertBinary = "0" + ComvertBinary; + } + } + + ResutValue = Integer.parseInt(ComvertBinary, 2) * -1; + break; + } + + + return ResutValue; + } + + /** + * 16진수 문자열(Hex String)을 Int배열로 변환 + * + * @param inputStr + * @return + */ + public static int[] hexStringToIntArray(String inputStr) { + int[] intArray; + int strLen = inputStr.length(); + + if (strLen % 2 != 0) { + inputStr = inputStr.substring(0, strLen - 1) + "0" + + inputStr.substring(strLen - 1, strLen); + strLen++; + } + + intArray = new int[strLen / 2]; + int ctr = 0; + for (int n = 0; n < strLen; n += 2) { + intArray[ctr] = Integer.valueOf(inputStr.substring(n, n + 2), 16); + ctr++; + } + + return intArray; + } + + + /** + * int[] --> CRC16CCITT + * + * @param intArray + * @return + */ + public static String CRC16CCITT(int[] intArray) { + int crc = 0x0000; // initial value + int polynomial = 0x1021; // 0001 0000 0010 0001 (0, 5, 12) + + for (int a : intArray) { + for (int i = 0; i < 8; i++) { + boolean bit = ((a >> (7-i) & 1) == 1); + boolean c15 = ((crc >> 15 & 1) == 1); + crc <<= 1; + if (c15 ^ bit) crc ^= polynomial; + } + } + + crc &= 0xFFFF; + String crcStr = Integer.toHexString(crc).toUpperCase(); + return crcStr; + } + + /** + * 바이너리스트링 --> CRC16CCITT + * + * @return + */ + public static String CRC16CCITT(String binary) { + int crc = 0x0000; // initial value + int polynomial = 0x1021; // 0001 0000 0010 0001 (0, 5, 12) + + byte[] bytes = binaryStringToByteArray(binary); + int[] intArray = new int[bytes.length]; + int ctr=0; + for(byte b : bytes){ + intArray[ctr] = b & 0xFF; + ctr++; + } + + for (int a : intArray) { + for (int i = 0; i < 8; i++) { + boolean bit = ((a >> (7-i) & 1) == 1); + boolean c15 = ((crc >> 15 & 1) == 1); + crc <<= 1; + if (c15 ^ bit) crc ^= polynomial; + } + } + + crc &= 0xFFFF; + String crcStr = Integer.toHexString(crc).toUpperCase(); + return crcStr; + } + + + /** + * CRC16_CCITT 생성함수 + * + * @param inputStr + * @param isBinary (inputStr 이 binary String 이면 true) + * @return + */ + public static String makeCRC16_CCITT(String inputStr , boolean isBinary) { + int crc = 0x0000; // initial value + int polynomial = 0x1021; // 0001 0000 0010 0001 (0, 5, 12) + int[] intArray; + int ctr; + String crcStr; + + // 입력 String 타입에 따른 int array 생성 + if (isBinary) { + byte[] bytes = binaryStringToByteArray(inputStr); + intArray = new int[bytes.length]; + ctr=0; + for(byte b : bytes){ + intArray[ctr] = b & 0xFF; + ctr++; + } + } else { + intArray = new int[inputStr.getBytes().length]; + ctr=0; + for(byte b : inputStr.getBytes()){ + intArray[ctr] = b; + ctr++; + } + } + + // crc16_ccitt 생성 로직 + for (int a : intArray) { + for (int i = 0; i < 8; i++) { + boolean bit = ((a >> (7-i) & 1) == 1); + boolean c15 = ((crc >> 15 & 1) == 1); + crc <<= 1; + if (c15 ^ bit) crc ^= polynomial; + } + } + crc &= 0xFFFF; + + // 입력 String 타입과 동일한 결과값 생성 + if (isBinary) { + // int 를 16자리 바이트 스트링으로 변경 + crcStr = String.format("%16s", Integer.toBinaryString(crc)).replace(' ', '0'); + + System.out.println("crcHex = "+Integer.toHexString(crc).toUpperCase()); + System.out.println("crcBinary = "+crcStr); + } else { + String crcHex = Integer.toHexString(crc).toUpperCase(); + System.out.println("crcHex = "+crcHex); + crcStr = hexToAscii(crcHex); + System.out.println("crcAscii = "+crcStr); + } + + + return crcStr; + } + + + /** + * CRC16 체크 함수 + * + * @param packet + * @param isBinary + * @return + */ + public static boolean checkCRC16_CCITT(String packet, boolean isBinary) { + String msg; + String crc; + boolean isCrc; + + if (isBinary) { + int len = packet.length(); + + msg = packet.substring(0, len-64); + crc = packet.substring(len-64, len-48); + } else { + String[] packetArray = packet.split("\\*"); + msg = packetArray[0]; + crc = packetArray[1]; + } + + String tempCrc = makeCRC16_CCITT(msg, isBinary); + + if (crc.equals(tempCrc)) isCrc = true; + else isCrc = false; + + return isCrc; + } + + + /** + * Hex String을 Ascii로 변환 + * + * @param hexStr + * @return + */ + public static String hexToAscii(String hexStr) { + StringBuilder output = new StringBuilder(""); + + for (int i = 0; i < hexStr.length(); i += 2) { + String str = hexStr.substring(i, i + 2); + output.append((char) Integer.parseInt(str, 16)); + } + + return output.toString(); + } + + /** + * 위경도를 도분초로 변환 + * + * @return string[] 도, 분, 초 + */ + public static String[] CoordinateToWGS84(Double LonLat, Boolean BLon) { + String[] result = new String[3]; + + double pDo = Math.floor(LonLat); + result[0] = String.format("%.0f", Math.abs(pDo)); + + double Temp = (Math.abs(LonLat) - Math.abs(pDo)) * 60.0; + + double pBun = Math.floor(Temp); + if (pBun == 0) { + result[1] = "00"; + } else { + result[1] = String.format("%.0f", pBun); + } + + Temp = (Temp - pBun) * 60.0; + double pCho = Temp; + + if (pCho == 0) { + result[2] = "00"; + } else { + result[2] = String.format("%.0f", pCho); + } + + return result; + } + + /** + * UTC 시간을 KST로 변환 + * + * @return + */ + public static String UtcToKst(String yyyyMMddHHmiss) { + int yyyy = Integer.parseInt(yyyyMMddHHmiss.substring(0 ,4)); + int MM = Integer.parseInt(yyyyMMddHHmiss.substring(4 ,6)); + int dd = Integer.parseInt(yyyyMMddHHmiss.substring(6 ,8)); + int HH = Integer.parseInt(yyyyMMddHHmiss.substring(8 ,10)); + int mi = Integer.parseInt(yyyyMMddHHmiss.substring(10,12)); + int ss = Integer.parseInt(yyyyMMddHHmiss.substring(12,14)); + + SimpleDateFormat dtformat = new SimpleDateFormat("yyyyMMddHHmmss"); + Calendar date = new GregorianCalendar(yyyy, MM-1, dd, HH, mi, ss); + date.add(Calendar.HOUR_OF_DAY, 9); + + Date data = date.getTime(); + String strDate = dtformat.format(data); + return strDate; + } + + /** + * UTC 시간을 KST로 변환(+특정 분) + * + * @return + */ + public static String UtcToKst_Before(String yyyyMMddHHmiss, int minutes) { + int yyyy = Integer.parseInt(yyyyMMddHHmiss.substring(0 ,4)); + int MM = Integer.parseInt(yyyyMMddHHmiss.substring(4 ,6)); + int dd = Integer.parseInt(yyyyMMddHHmiss.substring(6 ,8)); + int HH = Integer.parseInt(yyyyMMddHHmiss.substring(8 ,10)); + int mi = Integer.parseInt(yyyyMMddHHmiss.substring(10,12)); + int ss = Integer.parseInt(yyyyMMddHHmiss.substring(12,14)); + + SimpleDateFormat dtformat = new SimpleDateFormat("yyyyMMddHHmmss"); + Calendar date = new GregorianCalendar(yyyy, MM-1, dd, HH, mi, ss); + date.add(Calendar.MINUTE, minutes); + date.add(Calendar.HOUR_OF_DAY, 9); + + Date data = date.getTime(); + String strDate = dtformat.format(data); + return strDate; + } + + /** + * 문자형 시간 분 계산(+특정 분) + * + * @return + */ + public static String StrDateTimeIncMin(String yyyyMMddHHmiss, int minutes) { + int yyyy = Integer.parseInt(yyyyMMddHHmiss.substring(0 ,4)); + int MM = Integer.parseInt(yyyyMMddHHmiss.substring(4 ,6)); + int dd = Integer.parseInt(yyyyMMddHHmiss.substring(6 ,8)); + int HH = Integer.parseInt(yyyyMMddHHmiss.substring(8 ,10)); + int mi = Integer.parseInt(yyyyMMddHHmiss.substring(10,12)); + int ss = Integer.parseInt(yyyyMMddHHmiss.substring(12,14)); + + SimpleDateFormat dtformat = new SimpleDateFormat("yyyyMMddHHmmss"); + Calendar date = new GregorianCalendar(yyyy, MM-1, dd, HH, mi, ss); + date.add(Calendar.MINUTE, minutes); + + Date data = date.getTime(); + String strDate = dtformat.format(data); + return strDate; + } + public static String makeCRC(byte[] value) { + byte checkSum = 0; + + for(int i = 1; i < value.length; ++i) { + checkSum ^= value[i]; + } + + return String.format("%02X", checkSum); + } + + /** + * 1바이트 00 체크섬 생성 함수 + * + * @param s + * @return + */ + public static String makeCheckSum(String s) { + int mod = s.length() % 8; + int sum = 0; + String t=""; + + if (mod != 0) { + System.out.println("입력값이 바이트 단위가 아닙니다."); + } else { + for (int i=0; i i) { + if (bin.charAt(7-i) == '1') { + t = "1" + t; + pos = i; + } else { + t = "0" + t; + } + } else { + if (bin.charAt(7-i) == '1') { + t = "0" + t; + } else { + t = "1" + t; + } + } + } + + // checksum = Integer.parseInt(t, 2); + + // String CheckHex = Integer.toHexString(checksum).toUpperCase(); + } + + return t; + } + + /** + * 체크섬 검증 함수 + * + * @param s + * @return + */ + public static boolean checkCheckSum(String s) { + boolean isCheck; + String data = s.substring(0, s.length()-3); + String checksum = s.substring(s.length()-2, s.length()); + + String temp = makeCRC(data.toString().getBytes(StandardCharsets.UTF_8)); + + if (checksum.equals(temp)) { + isCheck = true; + } else { + isCheck = false; + } + + return isCheck; + } + + /** + * HexString To ByteArray + * + * @param s(Hex String) + * @return + */ + public static byte[] hexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16)); + } + return data; + } +} diff --git a/src/main/java/kr/gmtc/gw/comp/socket/vo/MsgObjVO.java b/src/main/java/kr/gmtc/gw/comp/socket/vo/MsgObjVO.java new file mode 100644 index 0000000..2851cef --- /dev/null +++ b/src/main/java/kr/gmtc/gw/comp/socket/vo/MsgObjVO.java @@ -0,0 +1,248 @@ +package kr.gmtc.gw.comp.socket.vo; + +import java.time.LocalDateTime; + +public class MsgObjVO { + + private int msgSeq; + // private String msg; + private byte[] msg; + private String recvIP; + private int recvPort; + private int recvID; + private int recvTag; + private LocalDateTime recvDT; + private int recvServerPort; + private String recvConnID; + + private String destIP; + private int destPort; + private String destID; + + private int sendID; + private int sendServerPort; + private String serviceClientIP; + private int serviceClientPort; + private String exceptIP; + private String sendClientIP; + private int sendClientPort; + private int serialPort; + + public MsgObjVO() { + msgSeq = 0; + msg = null; + recvIP = ""; + recvPort = 0; + recvID = -1; + recvTag = 0; + recvDT = LocalDateTime.now(); + + recvServerPort = -1; + recvConnID = ""; + destIP = ""; + destPort = 0; + destID = ""; + + sendID = -1; + sendServerPort = 0; + serviceClientIP = ""; + serviceClientPort = 0; + exceptIP = ""; + sendClientIP = ""; + sendClientPort = 0; + serialPort = 0; + } + + public int getMsgSeq() { + return msgSeq; + } + + public void setMsgSeq(int msgSeq) { + this.msgSeq = msgSeq; + } + + public byte[] getMsg() { + return msg; + } + + public void setMsg(byte[] msg) { + this.msg = msg; + } + + public String getRecvIP() { + return recvIP; + } + + public void setRecvIP(String recvIP) { + this.recvIP = recvIP; + } + + public int getRecvPort() { + return recvPort; + } + + public void setRecvPort(int rcvPort) { + this.recvPort = rcvPort; + } + + public int getRecvID() { + return recvID; + } + + public void setRecvID(int rcvID) { + this.recvID = rcvID; + } + + public LocalDateTime getRecvDT() { + return recvDT; + } + + public void setRecvDT(LocalDateTime localDateTime) { + this.recvDT = localDateTime; + } + + public int getRecvServerPort() { + return recvServerPort; + } + + public void setRecvServerPort(int serverPort) { + this.recvServerPort = serverPort; + } + + public String getRecvConnID() { + return recvConnID; + } + + public void setRecvConnID(String connID) { + this.recvConnID = connID; + } + + public int getRecvTag() { + return recvTag; + } + + public void setRecvTag(int tag) { + this.recvTag = tag; + } + + public String getDestIP() { + return destIP; + } + + public void setDestIP(String destIP) { + this.destIP = destIP; + } + + public int getDestPort() { + return destPort; + } + + public void setDestPort(int destPort) { + this.destPort = destPort; + } + + public String getDestID() { + return destID; + } + + public void setDestID(String destID) { + this.destID = destID; + } + + public int getSendID() { + return sendID; + } + + public void setSendID(int sendID) { + this.sendID = sendID; + } + + public int getSendServerPort() { + return sendServerPort; + } + + public void setSendServerPort(int serverPort) { + this.sendServerPort = serverPort; + } + + public String getServiceClientIP() { + return serviceClientIP; + } + + public void setServiceClientIP(String serviceClientIP) { + this.serviceClientIP = serviceClientIP; + } + + public int getServiceClientPort() { + return serviceClientPort; + } + + public void setServiceClientPort(int serviceClientPort) { + this.serviceClientPort = serviceClientPort; + } + + public String getExceptIP() { + return exceptIP; + } + + public void setExceptIP(String exceptIP) { + this.exceptIP = exceptIP; + } + + public String getSendClientIP() { + return sendClientIP; + } + + public void setSendClientIP(String clientIP) { + this.sendClientIP = clientIP; + } + + public int getSendClientPort() { + return sendClientPort; + } + + public void setSendClientPort(int clientPort) { + this.sendClientPort = clientPort; + } + + public int getSerialPort() { + return serialPort; + } + + public void setSerialPort(int serialPort) { + this.serialPort = serialPort; + } + + public MsgObjVO getClone() { + try { + MsgObjVO vo = new MsgObjVO(); + + vo.msgSeq = this.msgSeq; + vo.msg = this.msg; + vo.recvIP = this.recvIP; + vo.recvPort = this.recvPort;; + vo.recvID = this.recvID; + vo.recvTag = this.recvTag; + vo.recvDT = this.recvDT; + vo.recvServerPort = this.recvServerPort; + vo.recvConnID = this.recvConnID; + vo.destIP = this.destIP; + vo.destPort = this.destPort; + vo.destID = this.destID; + + vo.sendID = this.sendID; + vo.sendServerPort = this.sendServerPort; + vo.serviceClientIP = this.serviceClientIP; + vo.serviceClientPort = this.serviceClientPort; + vo.exceptIP = this.exceptIP; + vo.sendClientIP = this.sendClientIP; + vo.sendClientPort = this.sendClientPort; + vo.serialPort = this.serialPort; + + return vo; + } catch(Exception e) { + return null; + } + } + +} diff --git a/src/main/java/kr/gmtc/gw/comp/socket/vo/NetStatVO.java b/src/main/java/kr/gmtc/gw/comp/socket/vo/NetStatVO.java new file mode 100644 index 0000000..857cd3d --- /dev/null +++ b/src/main/java/kr/gmtc/gw/comp/socket/vo/NetStatVO.java @@ -0,0 +1,111 @@ +package kr.gmtc.gw.comp.socket.vo; + +import java.time.LocalDateTime; + +import kr.gmtc.gw.comp.socket.type.NetStatType; + +public class NetStatVO { + private LocalDateTime sendExecuteTime; + private LocalDateTime recvExecuteTime; + private LocalDateTime wbExecuteTime; + private LocalDateTime wouldBlockFromTime; + private LocalDateTime wouldBlockToTime; + private String description; + private LocalDateTime lastSendTime; + private LocalDateTime errorTime; + private int retryCount; + private NetStatType stat; + + public NetStatVO() { + sendExecuteTime = LocalDateTime.now(); + recvExecuteTime = LocalDateTime.now(); + wbExecuteTime = LocalDateTime.now(); + wouldBlockFromTime = LocalDateTime.now(); + wouldBlockToTime = LocalDateTime.now(); + description = ""; + lastSendTime = LocalDateTime.now(); + errorTime = LocalDateTime.now(); + retryCount = 0; + } + + public NetStatType getStat() { + return stat; + } + + public void setStat(NetStatType stat) { + this.stat = stat; + } + + public LocalDateTime getSendExecuteTime() { + return sendExecuteTime; + } + + public void setSendExecuteTime(LocalDateTime sendExecuteTime) { + this.sendExecuteTime = sendExecuteTime; + } + + public LocalDateTime getRecvExecuteTime() { + return recvExecuteTime; + } + + public void setRecvExecuteTime(LocalDateTime recvExecuteTime) { + this.recvExecuteTime = recvExecuteTime; + } + + public LocalDateTime getWbExecuteTime() { + return wbExecuteTime; + } + + public void setWbExecuteTime(LocalDateTime wbExecuteTime) { + this.wbExecuteTime = wbExecuteTime; + } + + public LocalDateTime getWouldBlockFromTime() { + return wouldBlockFromTime; + } + + public void setWouldBlockFromTime(LocalDateTime wouldBlockFromTime) { + this.wouldBlockFromTime = wouldBlockFromTime; + } + + public LocalDateTime getWouldBlockToTime() { + return wouldBlockToTime; + } + + public void setWouldBlockToTime(LocalDateTime wouldBlockToTime) { + this.wouldBlockToTime = wouldBlockToTime; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public LocalDateTime getLastSendTime() { + return lastSendTime; + } + + public void setLastSendTime(LocalDateTime lastSendTime) { + this.lastSendTime = lastSendTime; + } + + public LocalDateTime getErrorTime() { + return errorTime; + } + + public void setErrorTime(LocalDateTime errorTime) { + this.errorTime = errorTime; + } + + public int getRetryCount() { + return retryCount; + } + + public void setRetryCount(int retryCount) { + this.retryCount = retryCount; + } + +} diff --git a/src/main/java/kr/gmtc/gw/comp/thread/CustomThread.java b/src/main/java/kr/gmtc/gw/comp/thread/CustomThread.java new file mode 100644 index 0000000..db977f4 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/comp/thread/CustomThread.java @@ -0,0 +1,113 @@ +package kr.gmtc.gw.comp.thread; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import kr.gmtc.gw.comp.thread.handler.CustomThreadOnTerminate; +import kr.gmtc.gw.comp.thread.handler.CustomThreadWork; + +public class CustomThread extends Thread{ + + /* 대기시간 없음 */ + public static final long NO_SLEEP = 0; + /* 1밀리 초 */ + public static final long SLEEP_MILLI_SEC = 1; + /* 1초 */ + public static final long SLEEP_SECOND = 1000; + /* 30초 */ + public static final long SLEEP_HALF_MINIUTE = 30000; + /* 1분 */ + public static final long SLEEP_MINIUTE = 60000; + + public final String controllClassName; + public final long repeatMiliSec; + public final CustomThreadWork definedWork; + public final CustomThreadOnTerminate definedTerminate; + public final Logger logger; + private boolean running; + + /** + * 인터럽트를 받을 시 스레드가 종료됨.
+ * {@link Thread#sleep(long)} 기반으로 재실행 간격을 설정하므로 정확한 실행시간을 보장하지 않음.
+ * 정확한 실행시간 보장이 필요 할 경우 sleep 간격을 짧게 설정하고 호출위치에서 시간확인
+ * 정상적인 종료는 {@link #gracefulStop()}으로 종료함 + * @param threadName 스레드 이름 + * @param controllClass 스레드 관리 클래스, 일반적으로 this 사용 + * @param repeatMiliSec Sleep 시간(밀리 초), 0이하의 경우 대기시간 없음 + * @param definedWork 반복할 작업 + * @param definedTerminate 스레드가 인터럽트에 의해 종료될 경우 할 작업 + * @param autoStart 생성즉시 실행 + */ + + public CustomThread(String threadName, Object controllClass, long repeatMiliSec, + CustomThreadWork definedWork, CustomThreadOnTerminate definedTerminate, boolean autoStart) { + + if (definedWork == null) { + throw new IllegalArgumentException("[CustomThread] - definedWork is null."); + } + + this.definedWork = definedWork; + this.definedTerminate = definedTerminate; + this.controllClassName = controllClass == null ? "" : controllClass.getClass().getSimpleName(); + this.repeatMiliSec = repeatMiliSec > 0 ? repeatMiliSec : 0; + this.logger = LoggerFactory.getLogger(CustomThread.class); + this.running = false; + + setName(threadName); + setDaemon(true); + if (autoStart) { + this.start(); + } + } + + + @Override + public void run() { + logger.info("[CustomThread] Started."); + while ( this.running && !this.isInterrupted()) { + try { + try { + this.definedWork.work(); + } finally { + if (this.repeatMiliSec > 0) { + Thread.sleep(this.repeatMiliSec); + } + } + } catch(InterruptedException e) { // 인터럽트 수신시 종료 + logger.error("[CustomThread] Interrupted. "+ e.toString()); + Thread.currentThread().interrupt(); + break; + } catch(Exception e) { // 처리되지 않은 예외 로깅, 예외에 의한 무한루프에 주의 + logger.error("[CustomThread] Unknown Exception Occur. " + e.toString()); + } + } + + if(this.definedTerminate != null) { + this.definedTerminate.onTerminate(); + } + + logger.error("[CustomThread] Stoped."); + + } + + @Override + public String toString() { + + return "CustomThread [controllClass=" + this.controllClassName + ", threadName=" + getName() + + ", runnig=" + this.running + ", alive=" + isAlive()+ ", repeatMiliSec=" + this.repeatMiliSec + + ", definedTerminate=" + (this.definedTerminate == null ? "no" : "yes") + "]"; + } + + @Override + public synchronized void start() { + this.running = true; + super.start(); + } + + /** + * 스레드 정상종료, 진행중인 작업 완료 후 종료됨. + */ + public void gracefulStop() { + this.running = false; + } +} diff --git a/src/main/java/kr/gmtc/gw/comp/thread/handler/CustomThreadOnTerminate.java b/src/main/java/kr/gmtc/gw/comp/thread/handler/CustomThreadOnTerminate.java new file mode 100644 index 0000000..8f6930b --- /dev/null +++ b/src/main/java/kr/gmtc/gw/comp/thread/handler/CustomThreadOnTerminate.java @@ -0,0 +1,6 @@ +package kr.gmtc.gw.comp.thread.handler; + +@FunctionalInterface +public interface CustomThreadOnTerminate { + public void onTerminate(); +} diff --git a/src/main/java/kr/gmtc/gw/comp/thread/handler/CustomThreadWork.java b/src/main/java/kr/gmtc/gw/comp/thread/handler/CustomThreadWork.java new file mode 100644 index 0000000..4d3d156 --- /dev/null +++ b/src/main/java/kr/gmtc/gw/comp/thread/handler/CustomThreadWork.java @@ -0,0 +1,6 @@ +package kr.gmtc.gw.comp.thread.handler; + +@FunctionalInterface +public interface CustomThreadWork { + public void work() throws Exception; +} diff --git a/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 0000000..a1c16ce --- /dev/null +++ b/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,92 @@ +{"properties": [ + { + "name": "root", + "type": "java.lang.String", + "description": "A description for 'root'" + }, + { + "name": "asde.filepath1", + "type": "java.lang.String", + "description": "A description for 'asde.filepath1'" + }, + { + "name": "asde.filepath2", + "type": "java.lang.String", + "description": "A description for 'asde.filepath2'" + }, + { + "name": "asde.service", + "type": "java.lang.String", + "description": "A description for 'asde.service'" + }, + { + "name": "asde.service.queueCount", + "type": "java.lang.String", + "description": "A description for 'asde.queueCount'" + }, + { + "name": "asde.service.clearQ.max-count", + "type": "java.lang.String", + "description": "A description for 'asde.clearQ.max-count'" + }, + { + "name": "asde.service.clearQ.diff-time", + "type": "java.lang.String", + "description": "A description for 'asde.clearQ.diff-time'" + }, + { + "name": "asde.service.clearQ.clearTime", + "type": "java.lang.String", + "description": "A description for 'asde.clearQ.clearTime'" + }, + { + "name": "dev.debug.debugLogMode", + "type": "java.lang.String", + "description": "A description for 'dev.debug.debugLogMode'" + }, + { + "name": "database.db1.datasource.driver-class-name", + "type": "java.lang.String", + "description": "A description for 'database.db1.datasource.driver-class-name'" + }, + { + "name": "database.db1.datasource.password", + "type": "java.lang.String", + "description": "A description for 'database.db1.datasource.password'" + }, + { + "name": "database.db1.datasource.jdbcUrl", + "type": "java.lang.String", + "description": "A description for 'database.db1.datasource.jdbcUrl'" + }, + { + "name": "database.db1.datasource.username", + "type": "java.lang.String", + "description": "A description for 'database.db1.datasource.username'" + }, + { + "name": "kafka.settings", + "type": "java.lang.String", + "description": "A description for 'kafka.settings'" + }, + { + "name": "kafka.bootstrapAddress", + "type": "java.lang.String", + "description": "A description for 'kafka.bootstrapAddress'" + }, + { + "name": "kafka.consumer.group-id", + "type": "java.lang.String", + "description": "A description for 'kafka.consumer.group-id'" + }, + { + "name": "kafka.producer.key-serializer", + "type": "java.lang.String", + "description": "A description for 'kafka.producer.key-serializer'" + }, + { + "name": "kafka.producer.value-serializer", + "type": "java.lang.String", + "description": "A description for 'kafka.producer.value-serializer'" + } +]} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..b24a10a --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,71 @@ +spring: + profiles: + active: default + group: + default: + - winTest + +--- +spring: + config: + activate: + on-profile: default + +server: + port: 18080 + +asde: + service: + queueCount: 3 + serviceCount: 5000 + clearQ: + maxCount: 20000 # 2만건 + diffTime: 10000 # 10 sec + clearTime: 60000 # milli-Sec, 1분 + +state: + # 공통코드 CT001의 코드 6자리 + id: LK0401 + # 1:Primary, 2:Secondary + type: Primary + +--- +spring: + config: + activate: + on-profile: real + +root: /home/gmt/app/EyeGW_AsdeRecv + +--- +spring: + config: + activate: + on-profile: winTest + +# none = parsing 결과 표출 안함 +# line = parsing 결과 한줄 표시 +# detail = parsing 결과 상세 표시 +dev: + debug: + debugLogMode: none + +root: D:\Workspace\Odroid_repository\EyeGW_AsdeRecv + +database: + db1: + datasource: + driver-class-name: com.tmax.tibero.jdbc.TbDriver + jdbcUrl: jdbc:tibero:thin:@10.200.31.1:8629:sacp + username: ueai + password: ueai + +kafka: + settings: + bootstrapAddress: 118.220.143.175:9091,118.220.143.175:9091,118.220.143.176:9092 + consumer: + group-id: testgroup + producer: + key-serializer: org.apache.kafka.common.serialization.StringSerializer + value-serializer: org.springframework.kafka.support.serializer.JsonSerializer + diff --git a/src/main/resources/asterixList.xml b/src/main/resources/asterixList.xml new file mode 100644 index 0000000..11491ee --- /dev/null +++ b/src/main/resources/asterixList.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..83a9cf2 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + ${LOG_PATTERN} + + + + + + ${LOG_PATTERN} + + + + + + + + + + INFO + + + + ${LOG_PATH}${PATH_SEPARATOR}${LOG_FILE_NAME}.log + + + + ${FILE_LOG_PATTERN} + + + + + + ${LOG_PATH}${PATH_SEPARATOR}%d{yyyyMM,aux}${PATH_SEPARATOR}%d{yyyyMMdd}.log + + 10 + + 100mb + + true + + + + + + + false + 0 + 1024 + true + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/spec/alterixCat010Indra.yml b/src/main/resources/spec/alterixCat010Indra.yml new file mode 100644 index 0000000..352346a --- /dev/null +++ b/src/main/resources/spec/alterixCat010Indra.yml @@ -0,0 +1,735 @@ +cat: 10 +ver: 1.1_indra +dataitem: + - itemno: I010/010 + frn: 1 + rule: mandatory + itemdesc: Data Source Identifier + definition: Identification of the system from which the data are received. + format: fixed + octet: 2 + structure: + - name: SAC + frombit: 16 + tobit: 9 + desc: System Area Code fixed to zero + - name: SIC + frombit: 8 + tobit: 1 + desc: System Identification Code + - itemno: I010/020 + frn: 2 + rule: mandatory + itemdesc: Target Data Type (target descriptor) + definition: Type and characteristics of the data as transmitted by a system. + format: variable + octet: 1 + - itemno: I010/020-1 + frn: 201 + rule: mandatory + itemdesc: Target Report Descriptor + definition: Type and characteristics of the data as transmitted by a system. + format: fixed + octet: 1 + structure: + - name: TYP + frombit: 8 + desc: TYP + - name: DEC + frombit: 7 + tobit: 6 + desc: 00 default value for the SCA system. + - name: CTL + frombit: 5 + codes: + 0: disable the control function to perform calculation on the target + 1: enable the control function to perform calculation on the target + - name: VIS + frombit: 4 + desc: renamed from CHN (Chain 1/2) to VIS + codes: + 0: disable the label display + 1: enable the label display + - name: LAB + frombit: 3 + desc: renamed from GBS (Transponder ground) to LAB + codes: + 0: AUTOMATIC LABELLING + 1: MANUAL LABELLING + - name: TST + frombit: 2 + desc: renamed from CRT (Corrupted reply in multilateration) to TST + codes: + 0: real measure + 1: simulated data set + - name: EXT + frombit: 1 + codes: + 0: End of Data Item + 1: Extension into first extent + - itemno: I010/161 + frn: 3 + rule: optional + itemdesc: Track Number + definition: An integer value representing a unique reference to a track record within a particular track file. + format: fixed + octet: 2 + structure: + - name: TrkNb + serviceFieldName: track_no + frombit: 16 + tobit: 1 + max: 65535 + min: 0 + desc: Track number + - itemno: I010/040 + frn: 4 + rule: X + itemdesc: Target position in polar coordinates + definition: never present + format: fixed + octet: 4 + - itemno: I010/042 + frn: 5 + rule: optional + itemdesc: Position in Cartesian Coordinates + definition: Position of target in Cartesian Coordinates, in two’s complement form. + format: fixed + octet: 4 + structure: + - name: X-Component + serviceFieldName: lat + frombit: 32 + tobit: 17 + datatype: int + desc: X-Component + resolution: 0.25 + min: "-8191" + max: 8191 + unit: m + - name: Y-Component + serviceFieldName: lon + frombit: 16 + tobit: 1 + datatype: int + desc: Y-Component + resolution: 0.25 + min: "-8191" + max: 8191 + unit: m + - itemno: I010/200 + frn: 6 + rule: X + itemdesc: Target velocity in polar coordinates + definition: never present + format: fixed + octet: 2 + - itemno: I010/202 + frn: 7 + rule: optional + itemdesc: Velocity in Cartesian Coordinates + definition: Velocity in Cartesian Coordinates + format: fixed + octet: 2 + structure: + - name: Vx + serviceFieldName: spd + frombit: 16 + tobit: 9 + desc: Vx + resolution: 1 + min: "-127" + max: 127 + unit: m/s + datatype: int + - name: Vy + serviceFieldName: spd + frombit: 8 + tobit: 1 + desc: Vy + resolution: 1 + min: "-127" + max: 127 + unit: m/s + datatype: int + - itemno: I010/130 + frn: 8 + rule: optional + itemdesc: Track Information + definition: Track Information + format: variable + octet: 1 + - itemno: I010/130-1 + frn: 801 + rule: optional + itemdesc: Track Information + definition: Track Information + format: fixed + octet: 1 + structure: + - name: TLE + frombit: 8 + tobit: 6 + desc: target size + codes: + 7: Unknown size + - name: SHAPE + serviceFieldName: trgt_ty + frombit: 5 + tobit: 4 + desc: target size + codes: + 0: To be defined + 1: Vehicle (no Towing Plan linked) + 2: Aircraft (no Towing Plan linked) + 3: Towing (a Towing Plan is linked to the track). + - name: FLIGHT + frombit: 3 + tobit: 2 + desc: FLIGHT + codes: + 0: Other wise + 1: Inbound (arrival) + 2: Outbound (departure) + 3: De-icing, a departure aircraft taxying to de-icing zone or pad. + - name: EXT + frombit: 1 + tobit: 1 + desc: EXT + codes: + 0: No extension byte + 1: Extension byte + - itemno: I010/130-2 + frn: 802 + rule: optional + itemdesc: Track Information + definition: Track Information + format: fixed + octet: 1 + structure: + - name: XM + serviceFieldName: lat_add + frombit: 6 + tobit: 6 + max: 8191 + min: "-8191" + - name: YM + serviceFieldName: lon_add + frombit: 5 + tobit: 5 + max: 8191 + min: "-8191" + - name: VXM + frombit: 4 + tobit: 4 + max: 127 + min: "-127" + - name: VYM + frombit: 3 + tobit: 3 + max: 127 + min: "-127" + - itemno: I010/099 + frn: 9 + rule: X + itemdesc: Standard Instrumental Departure (SID) + definition: never present + format: fixed + octet: 8 + - itemno: I010/170 + frn: 10 + rule: X + itemdesc: Track Status + definition: never present + format: fixed + octet: 1 + - itemno: I010/141 + frn: 11 + rule: X + itemdesc: UTC time of day + definition: never present + format: fixed + octet: 3 + - itemno: I010/030 + frn: 12 + rule: X + itemdesc: Warning / Error + definition: never present + format: fixed + octet: 1 + - itemno: I010/270 + frn: 13 + rule: X + itemdesc: Target Size + definition: never present + format: fixed + octet: 1 + - itemno: I010/150 + frn: 14 + rule: X + itemdesc: Presence of X-Pulse + definition: never present + format: fixed + octet: 1 + - itemno: I010/070 + frn: 15 + rule: optional + itemdesc: Mode 3/A code (SSR Code) + definition: Mode-3/A code converted into octal representation. + format: fixed + octet: 2 + structure: + - name: V + frombit: 16 + desc: V + codes: + 0: Code validated + 1: Code not validated + - name: G + frombit: 15 + desc: G + codes: + 0: Default + 1: Garbled code + - name: L + frombit: 14 + desc: L + codes: + 0: Mode-2 code derived from the reply of the transponder + 1: Smoothed Mode-2 code as provided by a local tracker n + - name: spare + frombit: 13 + desc: Spare bit set to 0 + bitsConst: 0 + - name: Mod3A + serviceFieldName: ssr_cd + frombit: 12 + tobit: 1 + datatype: octal + desc: Mode-3/A reply in octal representation + - itemno: I010/072 + frn: 16 + rule: optional + itemdesc: Call Sign (Flight Number) + definition: The call sign is coded with 8 ASCII characters. + format: fixed + octet: 8 + structure: + - name: CallSign + serviceFieldName: clsgn + frombit: 64 + tobit: 1 + desc: CallSign + datatype: ascii + - itemno: I010/074 + frn: 17 + rule: optional + itemdesc: Mode S Address + definition: The mode S address is coded on three bytes. + format: fixed + octet: 3 + structure: + - name: ModeS + serviceFieldName: mode_s_cd + frombit: 24 + tobit: 1 + desc: ModeS + - itemno: I010/076 + frn: 18 + rule: optional + itemdesc: Registration Mark (Tail Number) + definition: The registration mark is coded with 8 ASCII characters. + format: fixed + octet: 8 + structure: + - name: Reg + serviceFieldName: tail_no + frombit: 64 + tobit: 1 + desc: Reg + datatype: ascii + - itemno: I010/300 + frn: 19 + rule: X + itemdesc: Position standard deviation in Cartesian Coordinates + definition: Position standard deviation in Cartesian coordinates + format: fixed + octet: 4 + - itemno: I010/302 + frn: 20 + rule: X + itemdesc: Velocity standard deviation in Cartesian coordinates + definition: never present + format: fixed + octet: 2 + - itemno: I010/032 + frn: 21 + rule: X + itemdesc: Label error + definition: never present + format: fixed + octet: 1 + - itemno: I010/090 + frn: 22 + rule: optional + itemdesc: Mode C Code (Height) + definition: Mode C height converted into binary representation on two bytes fixed length data item. + format: fixed + octet: 2 + structure: + - name: V + frombit: 16 + datatype: int + codes: + 0: Code validated + 1: Code not validated + - name: G + frombit: 15 + datatype: int + codes: + 0: Default value + 1: Garbled code + # - name: S + # frombit: 14 + # desc: sign bit + - name: FL + frombit: 14 + tobit: 1 + datatype: int + desc: Mode C HEIGHT + resolution: 0.25 + unit: FL + serviceFieldName: alt + - itemno: I010/044 + frn: 23 + rule: X + itemdesc: Target heading in degrees referenced to the North. + definition: never present + format: fixed + octet: 1 + - itemno: I010/078 + frn: 24 + rule: X + itemdesc: Aircraft Type + definition: never present + format: fixed + octet: 8 + - itemno: I010/080 + frn: 25 + rule: optional + itemdesc: Parking Position allocation + definition: Stand information for arrivals and/or de-icing zone/de-icing pad for departures that taxi for de-icing + format: fixed + octet: 4 + structure: + - name: STAND + frombit: 32 + tobit: 1 + desc: Parking Position allocation + datatype: ascii +# - name: Char2 +# frombit: 24 +# tobit: 17 +# desc: Char2 +# datatype: string +# - name: Char3 +# frombit: 16 +# tobit: 9 +# desc: Char3 +# datatype: string +# - name: Char4 +# frombit: 8 +# tobit: 1 +# desc: Char4 +# datatype: string + - itemno: I010/034 + frn: 26 + rule: X + itemdesc: Control alert + definition: never present + format: variable + octet: 1 + - itemno: I010/132 + frn: 27 + rule: X + itemdesc: Maneuver and Movement status + definition: never present + format: variable + octet: 1 + - itemno: I010/046 + frn: 28 + rule: X + itemdesc: Topological position and TopoObject-Id + definition: never present + format: fixed + octet: 10 + - itemno: I010/401 + frn: 29 + rule: optional + itemdesc: Time To Threshold + definition: Time To Threshold + format: variable + octet: 0 + - itemno: I010/401-1 + frn: 2901 + rule: optional + itemdesc: Time To Threshold + definition: Time To Threshold + format: fixed + octet: 2 + structure: + - name: SRC + frombit: 16 + tobit: 16 + desc: SRC + codes: + 0: TTT source is ARTS / Approach. + 1: TTT source is SDF. + - name: CST + frombit: 15 + tobit: 15 + desc: CST + codes: + 0: approach system or computed by SDF basing on an updated. + 1: extrapolated basing on computed. + - name: WRN + frombit: 14 + tobit: 14 + desc: WRN + codes: + 0: Normal condition. + 1: configurable threshold. + - name: TTT + frombit: 10 + tobit: 2 + desc: Seconds needed by aircraft to reach runway threshold. + unit: sec + max: 511 + min: 0 + - name: FX + frombit: 1 + tobit: 1 + codes: + 0: No additional bytes are present. + 1: Field extension. + - itemno: I010/401-2 + frn: 2902 + rule: optional + itemdesc: name of the runway + definition: name of the runway + format: fixed + octet: 3 + structure: + - name: NU1 + frombit: 24 + tobit: 17 + desc: First digit. + datatype: ascii + - name: NU2 + frombit: 16 + tobit: 9 + desc: Second digit. + datatype: ascii + - name: LTR + frombit: 8 + tobit: 1 + desc: Letter. + datatype: ascii + - itemno: I010/400 + frn: 35 + rule: mandatory + itemdesc: Identification of fused Sensors + definition: Identification of fused Sensors + format: variable + octet: 1 + - itemno: I010/400-1 + frn: 3501 + rule: mandatory + itemdesc: Identification of fused Sensors + definition: Identification of fused Sensors + format: fixed + octet: 1 + structure: + - name: S7 + frombit: 8 + desc: sensor7 + codes: + 0: target not detected by sensor + 1: target detected by sensor + - name: S6 + frombit: 7 + desc: sensor6 + codes: + 0: target not detected by sensor + 1: target detected by sensor + - name: S5 + frombit: 6 + desc: sensor5 + codes: + 0: target not detected by sensor + 1: target detected by sensor + - name: S4 + frombit: 5 + desc: sensor4 + codes: + 0: target not detected by sensor + 1: target detected by sensor + - name: S3 + frombit: 4 + desc: sensor3, target detected by ADS-B + codes: + 0: target not detected by sensor + 1: target detected by sensor + - name: S2 + frombit: 3 + desc: sensor2, target detected by Apron Control Tower SMR + codes: + 0: target not detected by sensor + 1: target detected by sensor + - name: S1 + frombit: 2 + desc: sensor1, target detected by ATCT SMR + codes: + 0: target not detected by sensor + 1: target detected by sensor + - name: EXT + frombit: 8 + desc: extension bit + - itemno: I010/081 + frn: 36 + rule: optional + itemdesc: Runway (RWY) + definition: Allocated Runway found in flight plan is set in this item. This item can deviate from the Runway threshold set in item I010/401 Time to Threshold. + format: fixed + octet: 3 + structure: + - name: RWY + frombit: 24 + tobit: 1 + desc: First ASCII character. + datatype: ascii +# - name: CHAR2 +# frombit: 16 +# tobit: 9 +# desc: Second ASCII character. +# datatype: string +# - name: CHAR3 +# frombit: 8 +# tobit: 1 +# desc: Third ASCII character. +# datatype: string + - itemno: I010/390 + frn: 37 + rule: X + itemdesc: Flight Plan Identifier (FPL-ID) + definition: never present + format: fixed + octet: 5 + - itemno: I010/082 + frn: 38 + rule: X + itemdesc: Actual Start Up Given Time (SUG) + definition: never present + format: fixed + octet: 2 + - itemno: I010/083 + frn: 39 + rule: X + itemdesc: Estimated/Actual Off-Block Time (EOFB/OFB) + definition: never present + format: fixed + octet: 2 + - itemno: I010/084 + frn: 40 + rule: optional + itemdesc: Estimated Time of Arrival (ETA) or Estimated Time of Departure (ETD) + definition: The Estimated Time of Arrival (ETA) or Estimated Time of Departure (ETD) + format: fixed + octet: 2 + structure: + - name: DAY + frombit: 16 + tobit: 15 + desc: DAY + codes: + 0: This day + 1: Last day + 2: Next day + 3: not used + - name: HOR + frombit: 14 + tobit: 9 + desc: Hours , from 0 to 23 + - name: ArrDep + frombit: 8 + tobit: 8 + desc: Arrival/Departure Flag + codes: + 0: Departure + 1: Arrival + - name: Spare + frombit: 7 + tobit: 7 + desc: Spare + - name: MIN + frombit: 6 + tobit: 1 + desc: Minutes, from 0 to 59 + - itemno: I010/085 + frn: 41 + rule: X + itemdesc: Estimated/Actual On-Block Time (EONB/ONB) + definition: never present + format: fixed + octet: 2 + - itemno: I010/391 + frn: 42 + rule: mandatory + itemdesc: Flight Plan Info (FPL-INFO) + definition: gives the CWP the information which action is allowed to execute from the controller on the track. + format: fixed + octet: 1 + structure: + - name: Drag&Drop + frombit: 8 + tobit: 8 + desc: Drag&Drop Assignment + codes: + 0: Forbidden + 1: Allowed + - name: Manual + frombit: 7 + tobit: 7 + desc: Manual Assignment + codes: + 0: Forbidden + 1: Allowed + - name: Deassignment + frombit: 6 + tobit: 6 + desc: Deassignment + codes: + 0: Forbidden + 1: Allowed + - name: Condition + frombit: 5 + tobit: 4 + desc: Condition + codes: + 0: No flight plan is correlated with the track + 1: Automatic identified track + 2: Drag&drop identified track + 3: Callsign manually assigned by operator + - name: Command + frombit: 3 + tobit: 2 + desc: Command + codes: + 0: deassign + 1: assign via automatic + 2: assign via drag&drop + 3: assign via manual + - name: SPA + frombit: 1 + tobit: 1 + desc: Spare Bit diff --git a/src/main/resources/spec/alterixCat011.yml b/src/main/resources/spec/alterixCat011.yml new file mode 100644 index 0000000..780a342 --- /dev/null +++ b/src/main/resources/spec/alterixCat011.yml @@ -0,0 +1,1645 @@ +# CAT-11 Transmission of A-SMGCS Data +cat: 11 +ver: 1.3 +dataitem: + - itemno: I011/010 + itemdesc: Data Source Identifier + frn: 1 + definition: "Identification of the radar station from which the data are received." + format: fixed + octet: 2 + structure: + - name: SAC + frombit: 16 + tobit: 9 + desc: System Area Code Fixed to Zero + - name: SIC + frombit: 8 + tobit: 1 + desc: System Identification Code + dataItemNote: "Note: The SAC is fixed to zero to indicate a data flow local to the airport." + - itemno: I011/000 + itemdesc: Message Type + frn: 2 + definition: "This Data Item allows for a more convenient + handling of the messagesat the receiver side by further defining + the type of transaction." + format: fixed + octet: 1 + structure: + - name: MT + frombit: 8 + tobit: 1 + desc: Message Type + codes: + 1: Target reports, flight plan data and basic alerts + 2: Manual attachment of flight plan to track + 3: Manual detachment of flight plan to track + 4: Insertion of flight plan data + 5: Suppression of flight plan data + 6: Modification of flight plan data + 7: Holdbar status + - itemno: I011/015 + itemdesc: Service Identification + frn: 3 + definition: "Identification of the service provided to one or more users." + octet: 1 + format: fixed + structure: + - name: SI + frombit: 8 + tobit: 1 + desc: Service Identification + dataItemNote: "Note: The service identification is allocated by the A-SMGCS" + - itemno: I011/140 + itemdesc: Time of Track Information + frn: 4 + definition: "Absolute time stamping expressed as UTC." + format: fixed + octet: 3 + structure: + - name: ToTI + frombit: 24 + tobit: 1 + datatype: uint + desc: Time of Track Information + resolution: 0.0078125 + unit: s + dataItemNote: "Note: The Time of Track Information value is reset to zero each day at midnight." + - itemno: I011/041 + itemdesc: Position in WGS-84 Coordinates + frn: 5 + definition: "Position of a target in WGS-84 Coordinates." + octet: 8 + format: fixed + structure: + - name: LAT + serviceFieldName: lat_wgs + frombit: 64 + tobit: 33 + datatype: int + desc: Latitude in WGS-84 in Twos Complement + resolution: 0.00000008381903171539306640625 + min: "-90" + max: 90 + unit: deg + - name: LON + serviceFieldName: lon_wgs + frombit: 32 + tobit: 1 + datatype: int + desc: Longitude in WGS-84 in Twos Complement + resolution: 0.00000008381903171539306640625 + min: "-180" + max: 180 + unit: deg + - itemno: I011/042 + itemdesc: Calculated Position in Cartesian Co-ordinates + frn: 6 + definition: "Calculated position of a target in Cartesian co-ordinates (twos complement form)." + format: fixed + octet: 4 + structure: + - name: X-Component + serviceFieldName: lat + frombit: 32 + tobit: 17 + datatype: int + desc: X-Component + resolution: 1.0 + min: "-32768" + max: 32768 + unit: m + - name: Y-Component + serviceFieldName: lon + frombit: 16 + tobit: 1 + datatype: int + desc: Y-Component + resolution: 1.0 + min: "-32768" + max: 32768 + unit: m + - itemno: I011/202 + itemdesc: Calculated Track Velocity in Cartesian Coordinates + frn: 7 + definition: "Calculated track velocity expressed in Cartesian co-ordinates." + format: fixed + octet: 4 + structure: + - name: VX + serviceFieldName: spd + frombit: 32 + tobit: 17 + desc: Vx + resolution: 0.25 + min: "-8192" + max: 8192 + unit: m/s + datatype: int + - name: VY + serviceFieldName: spd + frombit: 16 + tobit: 1 + desc: Vy + resolution: 0.25 + min: "-8192" + max: 8192 + unit: m/s + datatype: int + - itemno: I011/210 + itemdesc: Calculated Acceleration + frn: 8 + definition: "Calculated Acceleration of the target, in twos complement form." + format: fixed + octet: 2 + structure: + - name: AX + frombit: 16 + tobit: 9 + datatype: int + desc: Ax + resolution: 0.25 + min: "-31" + max: 31 + unit: m/s2 + - name: AY + frombit: 8 + tobit: 1 + datatype: int + desc: Ay + resolution: 0.25 + min: "-31" + max: 31 + unit: m/s2 + - itemno: I011/060 + itemdesc: Mode-3/A Code in Octal Representation + frn: 9 + definition: "Track Mode-3/A code converted into Octal Representation." + format: fixed + octet: 2 + structure: +# - name: spare +# frombit: 16 +# tobit: 13 +# desc: Spare bit(s) set to 0 + - name: MOD3A + serviceFieldName: ssr_cd + frombit: 12 + tobit: 1 + datatype: octal + desc: Mode-3/A Reply in Octal Representation + - itemno: I011/245 + itemdesc: Target Identification + frn: 10 + definition: "Target (aircraft or vehicle) identification in 8 characters." + format: fixed + octet: 7 + structure: + - name: STI + frombit: 56 + tobit: 55 + codes: + 0: Callsign or registration downlinked from transponder + 1: Callsign not downlinked from transponder + 2: Registration not downlinked from transponder +# - name: spare +# frombit: 54 +# tobit: 49 +# desc: Spare bit(s) set to 0 + - name: TID + serviceFieldName: trgt_id + frombit: 48 + tobit: 1 + datatype: 6bitschar + desc: Target Identification + dataItemNote: "Note: Characters 1-8 (coded on 6 bits each) defining target identification" + - itemno: I011/380 + itemdesc: Mode-S / ADS-B Related Data + frn: 11 + definition: "Data specific to Mode-S ADS-B." + format: compound + octet: 1 + - itemno: I011/380-1 + title: Mode S MB Data + frn: 1101 + definition: + format: repetitive + octet: 9 + structure: + - name: REP + desc: Repetition facto + frombit: 72 + tobit: 65 + - name: MB-data + desc: 56 bit message conveying Mode S B message data + frombit: 64 + tobit: 9 + datatype: ascii + - name: BDS1 + desc: Comm B data Buffer Store 1 Address + frombit: 8 + tobit: 5 + datatype: string + - name: BDS2 + desc: Comm B data Buffer Store 2 Address + frombit: 4 + tobit: 1 + datatype: string + dataItemNote: "Note: Repetitive starting with an one-octet Field Repetition Indicator (REP) followed by at least one BDS report comprising one seven octet BDS register and one octet BDS code" + - itemno: I011/380-2 + frn: 1102 + title: Aircraft Address + definition: Aircraft Address + format: fixed + octet: 3 + structure: + - name: ADR + serviceFieldName: mode_s_cd + frombit: 24 + tobit: 1 + desc: 24 bits Aircraft address, A23 to A0 +# - itemno: I011/380-3 # Never Sent + - itemno: I011/380-4 + frn: 1104 + title: Communications/ACAS Capability and Flight Status + definition: Communications/ACAS Capability and Flight Status + format: fixed + octet: 3 + structure: + - name: COM + frombit: 24 + tobit: 22 + desc: Communications capability of the ransponder + codes: + 0: No communications capability (surveillance only) + 1: Comm. A and Comm. B capability + 2: Comm. A, Comm. B and Uplink ELM + 3: Comm. A, Comm. B, Uplink ELM and Downlink ELM + 4: Level 5 Transponder capability + 5: Not assigned + 6: Not assigned + 7: Not assigned + - name: STAT + frombit: 21 + tobit: 18 + desc: Flight Status + codes: + 0: No alert, no SPI, aircraft airborne + 1: No alert, no SPI, aircraft on ground + 2: Alert, no SPI, aircraft airborne + 3: Alert, no SPI, aircraft on ground + 4: Alert, SPI, aircraft airborne or on ground + 5: No alert, SPI, aircraft airborne or on ground + 6: General Emergency + 7: Lifeguard / medical + 8: Minimum fuel + 9: No communications + 10: Unlawful interference +# - name: spare +# frombit: 17 +# tobit: 17 +# desc: Spare bit(s) set to 0 + - name: SSC + frombit: 16 + tobit: 16 + desc: Specific Service Capability + codes: + 0: No + 1: Yes + - name: ARC + frombit: 15 + tobit: 15 + desc: Altitude Reporting Capability + codes: + 0: 100 ft resolution + 1: 25 ft resolution + - name: AIC + frombit: 14 + tobit: 14 + desc: Aircraft Identification Capability + codes: + 0: No + 1: Yes + - name: B1A + frombit: 13 + tobit: 13 + desc: BDS 1,0 Bit 16 + - name: B1B + frombit: 12 + tobit: 9 + desc: BDS 1,0 Bit 37/40 + - name: AC + frombit: 8 + tobit: 8 + desc: ACAS Operational + codes: + 0: No + 1: Yes + - name: MN + frombit: 7 + tobit: 7 + desc: Multiple Navigational Aids Operating + codes: + 0: No + 1: Yes + - name: DC + frombit: 6 + tobit: 6 + desc: Differential Correction + codes: + 0: Yes + 1: No +# - name: spare +# frombit: 5 +# tobit: 1 +# desc: Spare bits set to 0 +# - itemno: I011/380-5 # Never Sent +# - itemno: I011/380-6 # Never Sent +# - itemno: I011/380-7 # Never Sent + - itemno: I011/380-8 + frn: 1108 + title: Aircraft Derived Aircraft Type + definition: Aircraft Derived Aircraft Type + format: fixed + octet: 4 + structure: + - name: ACT + frombit: 32 + tobit: 1 + datatype: ascii + desc: Aircraft Derived Aircraft Type + dataItemNote: "Note: Each of the four bytes composing the type of an aircraft contains an ASCII Character \n + (upper-case alphanumeric characters with trailing spaces)." + - itemno: I011/380-9 + frn: 1109 + title: Emitter Category + definition: Emitter Category + format: fixed + octet: 1 + structure: + - name: ECAT + desc: Emitter Category + frombit: 8 + tobit: 1 + codes: + 1: Light aircraft <= 7000 kg + 2: Reserved + 3: 7000 kg < medium aircraft < 136000 kg + 4: Reserved + 5: 136000 kg <= heavy aircraft + 6: Highly manoeuvrable (5g acceleration capability) and high speed (>400 knots cruise) + 7: Reserved + 8: Reserved + 9: Reserved + 10: Rotocraft + 11: Glider / sailplane + 12: Lighter-than-air + 13: Unmanned aerial vehicle + 14: Space / transatmospheric vehicle + 15: Ultralight / handglider / paraglider + 16: Parachutist / skydiver + 17: Reserved + 18: Reserved + 19: Reserved + 20: Surface emergency vehicle + 21: Surface service vehicle + 22: Fixed ground or tethered obstruction + 23: Reserved + 24: Reserved +# - itemno: I011/380-10 # Never Sent + - itemno: I011/380-11 + frn: 1111 + title: Available Technologies + definition: Available Technologies + format: fixed + octet: 1 + structure: + - name: VDL + frombit: 8 + tobit: 8 + desc: VDL Mode 4 + codes: + 0: VDL Mode 4 available + 1: VDL Mode 4 not available + - name: MDS + frombit: 7 + tobit: 7 + desc: Mode S + codes: + 0: Mode S available + 1: Mode S not available + - name: UAT + frombit: 6 + tobit: 6 + desc: UAT + codes: + 0: UAT available + 1: UAT not available +# - name: spare +# frombit: 5 +# tobit: 1 +# desc: Spare bit(s) set to 0 + - itemno: I011/161 + frn: 12 + itemdesc: Track Number + definition: "Identification of a fusion track (single track number)." + format: fixed + octet: 2 + structure: +# - name: spare +# frombit: 16 +# desc: Spare bit(s) set to 0 + - name: FTN + serviceFieldName: track_no + frombit: 15 + tobit: 1 + desc: Fusion Track Number + - itemno: I011/170 + itemdesc: Track Status + frn: 13 + definition: "Status of track." + format: variable + octet: 1 + - itemno: I011/170-1 + itemdesc: Track Status + frn: 1301 + definition: "Status of track." + format: fixed + octet: 1 + structure: + - name: MON + frombit: 8 + tobit: 8 + codes: + 0: Multisensor Track + 1: Monosensor Track + - name: GBS + frombit: 7 + tobit: 7 + codes: + 0: Transponder Ground bit not set or unknown + 1: Transponder Ground bit set + - name: MRH + frombit: 6 + tobit: 6 + codes: + 0: Barometric altitude (Mode C) more reliable + 1: Geometric altitude more reliable + - name: SRC + frombit: 5 + tobit: 3 + codes: + 0: No source + 1: GPS + 2: 3d radar + 3: Triangulation + 4: Height from coverage + 5: Speed look-up table + 6: Default height + 7: Multilateration + - name: CNF + frombit: 2 + tobit: 2 + codes: + 0: Confirmed track + 1: Tentative track + - name: FX + frombit: 1 + tobit: 1 + desc: Extension Indicator + codes: + 0: End of Data Item + 1: Extension into first extent + - itemno: I011/170-2 + itemdesc: Track Status + frn: 1302 + definition: First Extension of Data Item. + format: fixed + octet: 1 + structure: + - name: SIM + frombit: 8 + tobit: 8 + codes: + 0: Actual Track + 1: Simulated track + - name: TSE + frombit: 7 + tobit: 7 + codes: + 0: Default value + 1: Track service end (i.e. last message transmitted to the user for the track) + - name: TSB + frombit: 6 + tobit: 6 + codes: + 0: Default value + 1: Track service begin (i.e. first message transmitted to the user for the track) + - name: FRIFOE + frombit: 5 + tobit: 4 + codes: + 0: No Mode 4 interrogationt + 1: Friendly target + 2: Unknown target + 3: No reply + - name: ME + frombit: 3 + tobit: 3 + codes: + 0: Default value + 1: Military Emergency present in the last report received from a sensor capable of decoding this data + - name: MI + frombit: 2 + tobit: 2 + codes: + 0: Default value + 1: Military Identification present in the last report received from a sensor capable of decoding this data + - name: FX + frombit: 1 + tobit: 1 + desc: Extension Indicator + codes: + 0: End of Data Item + 1: Extension + - itemno: I011/170-3 + itemdesc: Track Status + frn: 1303 + definition: Second Extension of Data Item. + format: fixed + octet: 1 + structure: + - name: AMA + frombit: 8 + codes: + 0: Track not resulting from amalgamation process + 1: Track resulting from amalgamation process + - name: SPI + frombit: 7 + codes: + 0: Default value + 1: SPI present in the last report received from a sensor capable of decoding this data + - name: CST + frombit: 6 + codes: + 0: Default value + 1: Age of the last received track update is higher than system dependent threshold (coasting) + - name: FPC + frombit: 5 + codes: + 0: Not flight-plan correlated + 1: Flight plan correlated + - name: AFF + frombit: 4 + codes: + 0: Default value + 1: ADS-B data inconsistent with other surveillance information +# - name: spare +# frombit: 3 +# tobit: 2 +# desc: Spare bit(s) set to 0 + - name: FX + frombit: 1 + tobit: 1 + desc: Extension Indicator + codes: + 0: End of Data Item + 1: Extension + - itemno: I011/170-4 + itemdesc: Track Status + frn: 1304 + definition: Third Extension of Data Item + format: fixed + octet: 1 + structure: +# - name: spare +# frombit: 8 +# desc: Spare bit(s) set to 0 + - name: PSR + frombit: 7 + codes: + 0: Default value + 1: Age of the last received PSR track update is higher than system dependent threshold + - name: SSR + frombit: 6 + codes: + 0: Default value + 1: Age of the last received SSR track update is higher than system dependent threshold + - name: MDS + frombit: 5 + codes: + 0: Default value + 1: Age of the last received Mode S track update is higher than system dependent threshold + - name: ADS + frombit: 4 + codes: + 0: Default value + 1: Age of the last received ADS track update is higher than system dependent threshold + - name: SUC + frombit: 3 + codes: + 0: Default value + 1: Special Used Code (Mode A codes to be defined in the system to mark a track with special interest) + - name: AAC + frombit: 2 + codes: + 0: Default value + 1: Assigned Mode A Code Conflict (same individual Mode A Code assigned to another track) + - name: FX + frombit: 1 + tobit: 1 + desc: Extension Indicator + codes: + 0: End of Data Item + 1: Extension + dataItemNote: "Track type and coasting can also be derived from Data Item I011/290 System Track Update Ages" + - itemno: I011/290 + itemdesc: System Track Update Ages + frn: 14 + definition: "Ages of the last plot/local track, or the last valid mode-A/mode-C, used to update the system track." + format: compound + octet: 2 + - itemno: I011/290-1 + itemdesc: System Track Update Ages + frn: 1401 + definition: PSR Age. + format: fixed + octet: 1 + structure: + - name: PSR + frombit: 8 + tobit: 1 + datatype: uint + desc: Age of the Last Primary Report Used to Update the Track + resolution: 0.25 + unit: s + - itemno: I011/290-2 + itemdesc: System Track Update Ages + frn: 1402 + definition: SSR Age. + format: fixed + octet: 1 + structure: + - name: SSR + frombit: 8 + tobit: 1 + datatype: uint + desc: Age of the Last Secondary Report Used to Update the Track + resolution: 0.25 + unit: s + - itemno: I011/290-3 + itemdesc: System Track Update Ages + frn: 1403 + definition: MDA Age. + format: fixed + octet: 1 + structure: + - name: MDA + frombit: 8 + tobit: 1 + datatype: uint + desc: Age of the Last Valid Mode A Report Used to Update the Track + resolution: 0.25 + unit: s + - itemno: I011/290-4 + itemdesc: System Track Update Ages + frn: 1404 + definition: MFL Age. + format: fixed + octet: 1 + structure: + - name: MFL + frombit: 8 + tobit: 1 + datatype: uint + desc: Age of the Last Valid and Credible Mode C Used to Update the + Track + resolution: 0.25 + unit: s + - itemno: I011/290-5 + itemdesc: System Track Update Ages + frn: 1405 + definition: MDS Age. + format: fixed + octet: 1 + structure: + - name: MDS + frombit: 8 + tobit: 1 + datatype: uint + desc: Age of the Last Mode S Report Used to Update the Track + resolution: 0.25 + unit: s + - itemno: I011/290-6 + itemdesc: System Track Update Ages + frn: 1406 + definition: ADS Age. + format: fixed + octet: 2 + structure: + - name: ADS + frombit: 16 + tobit: 1 + datatype: uint + desc: Age of the Last ADS Report Used to Update the Track + resolution: 0.25 + unit: s + - itemno: I011/290-7 + itemdesc: System Track Update Ages + frn: 1407 + definition: ADB Age. + format: fixed + octet: 1 + structure: + - name: ADB + frombit: 8 + tobit: 1 + datatype: uint + desc: Age of the Last ADS-B Report Used to Update the Track + resolution: 0.25 + unit: s + - itemno: I011/290-8 + itemdesc: System Track Update Ages + frn: 1408 + definition: MD1 Age. + format: fixed + octet: 1 + structure: + - name: MD1 + frombit: 8 + tobit: 1 + datatype: uint + desc: Age of the Last Valid Mode 1 Used to Update the Track + resolution: 0.25 + unit: s + - itemno: I011/290-9 + itemdesc: System Track Update Ages + frn: 1409 + definition: MD2 Age. + format: fixed + octet: 1 + structure: + - name: MD2 + frombit: 8 + tobit: 1 + datatype: uint + desc: Age of the Last Valid Mode 2 Used to Update the Track + resolution: 0.25 + unit: s + - itemno: I011/290-10 + itemdesc: System Track Update Ages + frn: 1410 + definition: LOP Age. + format: fixed + octet: 1 + structure: + - name: LOP + frombit: 8 + tobit: 1 + datatype: uint + desc: Age of the Last Magentic Loop Detection + resolution: 0.25 + unit: s + - itemno: I011/290-11 + itemdesc: System Track Update Ages + frn: 1411 + definition: TRK Age. + format: fixed + octet: 1 + structure: + - name: TRK + frombit: 8 + tobit: 1 + datatype: uint + desc: Actual Track Age Since First Occurrence + resolution: 0.25 + unit: s + - itemno: I011/290-12 + itemdesc: System Track Update Ages + frn: 1412 + definition: MUL Age. + format: fixed + octet: 1 + structure: + - name: MUL + frombit: 8 + tobit: 1 + datatype: uint + desc: Age of the Last Multilateration Detection + resolution: 0.25 + unit: s + dataItemNote: "Note: The ages are counted from Data Item I011/140, Time Of Track Information, \n + using the following formula: Age = Time of track information - Time of last (valid) \n + update If the computed age is greater than the maximum value \n + or if the data has never been received, then the corresponding subfield is not sent." + - itemno: I011/430 + itemdesc: Phase of Flight + frn: 15 + definition: "Current phase of the flight." + format: fixed + octet: 1 + structure: + - name: PoF + frombit: 8 + tobit: 1 + desc: Phase of Flight + codes: + 0: Unknown + 1: On stand + 2: Taxiing for departure + 3: Taxiing for arrival + 4: Runway for departure + 5: Runway for arrival + 6: Hold for departure + 7: Hold for arrival + 8: Push back + 9: On finals + - itemno: I011/090 + itemdesc: Measured Flight Level + frn: 16 + definition: "Last valid and credible flight level used to update the track, in twos complement representation." + format: fixed + octet: 2 + structure: + - name: MFL + frombit: 16 + tobit: 1 + datatype: int + desc: Measured Flight Level + resolution: 0.25 + min: "-12" + max: 1500 + unit: FL + serviceFieldName: alt + dataItemNote: "Note: The criteria to determine + the credibility of the flight level are Tracker dependent. Credible + means: within reasonable range of change with respect to the previous detection." + - itemno: I011/093 + itemdesc: Calculated Track Barometric Altitude + frn: 17 + definition: "Calculated Barometric Altitude of the track." + format: fixed + octet: 2 + structure: + - name: QNH + frombit: 16 + desc: QNH Correction Applied + codes: + 0: No QNH Correction Applied + 1: QNH Correction Applied + - name: CTBA + frombit: 15 + tobit: 1 + datatype: int + desc: Calculated Track Barometric Altitude + resolution: 0.25 + min: "-15" + max: 1500 + unit: FL + - itemno: I011/092 + itemdesc: Calculated Track Geometric Altitude + frn: 18 + definition: "Calculated geometric vertical distance above mean sea level, not related to barometric pressure." + format: fixed + octet: 2 + structure: + - name: CTGA + frombit: 16 + tobit: 1 + desc: Calculated Track Geometric Altitude + resolution: 6.25 + min: "-1500" + max: 150000 + unit: ft + dataItemNote: "Note: The source of altitude is identified in bits (SRC) of item I011/170 Track Status." + - itemno: I011/215 + itemdesc: Calculated Rate Of Climb/Descent + frn: 19 + definition: "Calculated rate of Climb/Descent of an aircraft, in twos complement form." + format: fixed + octet: 2 + structure: + - name: CROC + frombit: 16 + tobit: 1 + datatype: int + desc: Calculated Rate Of Climb/Descent + resolution: 6.25 + min: "-204800" + max: 204800 + unit: ft/min + - itemno: I011/270 + itemdesc: Target Size and Orientation + frn: 20 + definition: "Target size defined as length and with of the detected target, and orientation." + format: variable + octet: 1 + - itemno: I011/270-1 + itemdesc: Target Size and Orientation + frn: 2001 + definition: First Part + format: fixed + octet: 1 + structure: + - name: LENGTH + frombit: 8 + tobit: 2 + datatype: uint + desc: Length + resolution: 1.0 + unit: m + - name: FX + frombit: 1 + tobit: 1 + desc: Extension Indicator + codes: + 0: End of Data Item + 1: Extension + - itemno: I011/270-2 + itemdesc: Target Size and Orientation + frn: 2002 + definition: First Part + format: fixed + octet: 1 + structure: + - name: ORIENTATION + serviceFieldName: cos + frombit: 8 + tobit: 2 + datatype: uint + desc: Orientation + resolution: 2.8125 + unit: deg + - name: FX + frombit: 1 + fx: 1 + desc: Extension Indicator + codes: + 0: End of Data Item + 1: Extension + - itemno: I011/270-3 + itemdesc: Target Size and Orientation + frn: 2003 + definition: Second Extent + format: fixed + octet: 1 + structure: + - name: WIDTH + frombit: 8 + tobit: 2 + datatype: uint + desc: Width + resolution: 1.0 + unit: m + - name: FX + frombit: 1 + tobit: 1 + desc: Extension Indicator + codes: + 0: End of Data Item + 1: Extension + dataItemNote: "Note: The orientation gives the direction to which the aircraft nose is pointing, relative to the Geographical North." + - itemno: I011/390 + itemdesc: Flight Plan Related Data + frn: 21 + definition: "All flight plan related information." + format: compound + octet: 2 + - itemno: I011/390-1 + itemdesc: Flight Plan Related Data + frn: 2101 + definition: FPPS Identification Tag + format: fixed + octet: 1 + structure: + - name: SAC + frombit: 16 + tobit: 9 + desc: System Area Code + - name: SIC + frombit: 8 + tobit: 1 + desc: System Identity Code + - itemno: I011/390-2 + itemdesc: Flight Plan Related Data + frn: 2102 + definition: Callsign + format: fixed + octet: 7 + structure: + # - name: CSN + - name: CallSign + serviceFieldName: clsgn + frombit: 56 + tobit: 1 + datatype: ascii + desc: Callsign + dataItemNote: "note: Each of the seven Octets contains an ASCII Character. The Callsign is always left adjusted. It contains up to seven upper-case alphanumeric characters, the remaining character positions (if any) are padded with space characters." + - itemno: I011/390-3 + itemdesc: Flight Plan Related Data + frn: 2103 + definition: IFPS_FLIGHT_ID + format: fixed + octet: 4 + structure: + - name: TYP + frombit: 32 + tobit: 31 + desc: IFPS Flight ID Type + codes: + 0: Plan number + 1: Unit 1 internal flight number + 2: Unit 2 internal flight number + 3: Unit 3 internal flight number +# - name: spare +# frombit: 30 +# tobit: 28 +# desc: Spare bit(s) set to 0 + - name: NBR + frombit: 27 + tobit: 1 + desc: IFPS Flight ID Number + - itemno: I011/390-4 + itemdesc: Flight Plan Related Data + frn: 2104 + definition: Flight Category + format: fixed + octet: 1 + structure: + - name: GATOAT + frombit: 8 + tobit: 7 + desc: Flight Type + codes: + 0: Unknown + 1: General Air Traffic + 2: Operational Air Traffic + 3: Not applicable + - name: FR1FR2 + frombit: 6 + tobit: 5 + desc: Flight Rules + codes: + 0: Instrument Flight Rules + 1: Visual Flight Rules + 2: Not applicable + 3: Controlled Visual Flight Rules + - name: RVSM + frombit: 4 + tobit: 3 + desc: RVSM + codes: + 0: Unknown + 1: Approved + 2: Exempt + 3: Not Approved + - name: HPR + frombit: 2 + desc: Flight Priority + codes: + 0: Normal Priority Flight + 1: High Priority Flight +# - name: spare +# frombit: 1 +# desc: Spare bit(s) set to 0 + - itemno: I011/390-5 + itemdesc: Flight Plan Related Data + frn: 2105 + definition: Type of Aircraft + format: fixed + octet: 4 + structure: + - name: TOA + frombit: 32 + tobit: 1 + datatype: ascii + desc: Type of Aircraft + - itemno: I011/390-6 + itemdesc: Flight Plan Related Data + frn: 2106 + definition: Wake Turbulence Category + format: fixed + octet: 1 + structure: + - name: WTC + frombit: 8 + tobit: 1 + desc: Wake Turbulence Category + codes: + 72: Heavy # H + 74: Super # J + 76: Light # L + 77: Medium # M + - itemno: I011/390-7 + itemdesc: Flight Plan Related Data + frn: 2107 + definition: Departure Airport + format: fixed + octet: 4 + structure: + - name: ADEP + frombit: 32 + tobit: 1 + datatype: ascii + desc: Departure Airport + - itemno: I011/390-8 + itemdesc: Flight Plan Related Data + frn: 2108 + definition: Destination Airport + format: fixed + octet: 4 + structure: + - name: ADES + frombit: 32 + tobit: 1 + datatype: ascii + desc: Destination Airport + - itemno: I011/390-9 + itemdesc: Flight Plan Related Data + frn: 2109 + definition: Runway Designation + format: fixed + octet: 3 + structure: + - name: RWY + frombit: 24 + tobit: 1 + datatype: ascii + desc: Runway Designation + - itemno: I011/390-10 + itemdesc: Flight Plan Related Data + frn: 2110 + definition: Current Cleared Flight Level + format: fixed + octet: 2 + structure: + - name: CFL + frombit: 16 + tobit: 1 + datatype: uint + desc: Current Cleared Flight Level + resolution: 0.25 + unit: FL + - itemno: I011/390-11 + itemdesc: Flight Plan Related Data + frn: 2111 + definition: Current Cleared Flight Level + format: fixed + octet: 2 + structure: + - name: CENTRE + frombit: 16 + tobit: 9 + desc: 8-bit Group Identification Code + - name: POSITION + frombit: 8 + tobit: 1 + desc: 8-bit Control Position Identification Code + - itemno: I011/390-12 + itemdesc: Flight Plan Related Data + frn: 2112 + definition: Time of Departure + format: repetitive + octet: 5 + structure: + - name: REP + frombit: 40 + tobit: 33 + desc: Repetition Factor + - name: TYP + frombit: 32 + tobit: 28 + desc: Time Type + codes: + 0: Scheduled off-block time + 1: Estimated off-block time + 2: Estimated take-off time + 3: Actual off-block time + 4: Predicted time at runway hold + 5: Actual time at runway hold + 6: Actual line-up time + 7: Actual take-off time + 8: Estimated time of arrival + 9: Predicted landing time + 10: Actual landing time + 11: Actual time off runway + 12: Predicted time to gate + 13: Actual on-block time + - name: DAY + frombit: 27 + tobit: 26 + desc: Day + codes: + 0: Today + 1: Yesterday + 2: Tomorrow +# - name: spare +# frombit: 25 +# tobit: 22 +# desc: spare bits set to zero + - name: HOR + frombit: 21 + tobit: 17 + datatype: uint + desc: Hours, from 0 to 23 + min: 0 + max: 23 +# - name: spare +# frombit: 16 +# tobit: 15 +# desc: Spare bit(s) set to 0 + - name: MIN + frombit: 14 + tobit: 9 + datatype: uint + desc: Minutes, from 0 to 59 + min: 0 + max: 59 + - name: AVS + frombit: 8 + desc: Seconds Available + codes: + 0: Seconds available + 1: Seconds not available +# - name: spare +# frombit: 7 +# desc: Spare bit(s) set to 0 + - name: SEC + frombit: 6 + tobit: 1 + datatype: uint + desc: Seconds, from 0 to 59 + min: 0 + max: 59 + - itemno: I011/390-13 + itemdesc: Flight Plan Related Data + frn: 2113 + definition: Aircraft Stand + format: fixed + octet: 6 + structure: + - name: AST + frombit: 48 + tobit: 1 + datatype: ascii + desc: Aircraft Stand + - itemno: I011/390-14 + itemdesc: Flight Plan Related Data + frn: 2114 + definition: Stand Status + format: fixed + octet: 1 + structure: + - name: EMP + frombit: 8 + tobit: 7 + desc: Stand Empty + codes: + 0: Empty + 1: Occupied + 2: Unknown + - name: AVL + frombit: 6 + tobit: 5 + desc: Stand Available + codes: + 0: Available + 1: Not available + 2: Unknown +# - name: spare +# frombit: 4 +# tobit: 1 + - itemno: I011/300 + itemdesc: Vehicle Fleet Identification + frn: 22 + definition: "Vehicle fleet identification number." + format: fixed + octet: 1 + structure: + - name: VFI + frombit: 8 + tobit: 1 + desc: Vehicle Fleet Identification + codes: + 0: Flyco (follow me) + 1: ATC equipment maintenance + 2: Airport maintenance + 3: Fire + 4: Bird scarer + 5: Snow plough + 6: Runway sweeper + 7: Emergency + 8: Police + 9: Bus + 10: Tug (push/tow) + 11: Grass cutter + 12: Fuel + 13: Baggage + 14: Catering + 15: Aircraft maintenance + 16: Unknown + serviceFieldName: carty + - itemno: I011/310 + itemdesc: Pre-programmed Message + frn: 23 + definition: "Number related to a pre-programmed message that can be transmitted by a vehicle." + format: fixed + octet: 1 + structure: + - name: TRB + frombit: 8 + tobit: 8 + desc: In Trouble + codes: + 0: Default + 1: In Trouble + - name: MSG + frombit: 7 + tobit: 1 + desc: Message + codes: + 1: Towing aircraft + 2: FOLLOW-ME operation + 3: Runway check + 4: Emergency operation (fire, medical...) + 5: Work in progress (maintenance, birds scarer, sweepers...) + - itemno: I011/500 + itemdesc: Estimated Accuracies + frn: 24 + definition: "Overview of all important accuracies (standard deviations)." + octet: 1 + format: compound + - itemno: I011/500-1 + itemdesc: Estimated Accuracies + frn: 2401 + definition: Estimated Accuracy Of Track Position (Cartesian) + octet: 2 + format: fixed + structure: + - name: APC-X + frombit: 16 + tobit: 9 + datatype: uint + desc: Estimated Accuracy of the Calculated Position of X Component + resolution: 0.25 + unit: m + - name: APC-Y + frombit: 8 + tobit: 1 + datatype: uint + desc: Estimated Accuracy of the Calculated Position of Y Component + resolution: 0.25 + unit: m + - itemno: I011/500-2 + itemdesc: Estimated Accuracies + frn: 2402 + definition: Estimated Accuracy Of Track Position (WGS84) + octet: 4 + format: fixed + structure: + - name: APW-LAT + frombit: 32 + tobit: 17 + datatype: int + desc: APW Latitude Component Accuracy + resolution: 0.00000008381903171539306640625 + unit: deg + - name: APW-LON + frombit: 16 + tobit: 1 + datatype: int + desc: APW Longitude Component Accuracy + resolution: 0.00000008381903171539306640625 + unit: deg + - itemno: I011/500-3 + itemdesc: Estimated Accuracies + frn: 2403 + definition: Estimated Accuracy Of Height + octet: 2 + format: fixed + structure: + - name: ATH + frombit: 16 + tobit: 1 + datatype: int + desc: Estimated Accuracy Of the calculated altitude of an aircraft. + resolution: 0.5 + unit: m + - itemno: I011/500-4 + itemdesc: Estimated Accuracies + frn: 2404 + definition: Estimated Accuracy Of Track Velocity (Cartesian) + octet: 2 + format: fixed + structure: + - name: AVC-X + frombit: 16 + tobit: 9 + datatype: uint + desc: Estimated Accuracy of the Calculated Velocity of X Component + resolution: 0.1 + unit: m/s + - name: Y + frombit: 8 + tobit: 1 + datatype: uint + desc: Estimated Accuracy of the Calculated Velocity of Y Component + resolution: 0.1 + unit: m/s + - itemno: I011/500-5 + itemdesc: Estimated Accuracies + frn: 2405 + definition: Estimated Accuracy Of Rate Of Climb / Descent + octet: 2 + format: fixed + structure: + - name: ARC + frombit: 16 + tobit: 1 + datatype: int + desc: Estimated Accuracy Of Rate Of Climb / Descent + resolution: 0.1 + unit: m/s + - itemno: I011/500-6 + itemdesc: Estimated Accuracies + frn: 2406 + definition: Estimated Accuracy Of Acceleration (Cartesian) + octet: 2 + format: fixed + structure: + - name: AAC-X + frombit: 16 + tobit: 9 + datatype: uint + desc: Estimated Accuracy Of Acceleration of X Component + resolution: 0.01 + unit: m/s2 + - name: AAC-Y + frombit: 8 + tobit: 1 + datatype: uint + desc: Estimated Accuracy Of Acceleration of Y Component + resolution: 0.01 + unit: m/s2 + - itemno: I011/600 + itemdesc: Alert Messages + frn: 25 + definition: "Alert involving the targets indicated in I011/605." + format: fixed + octet: 3 + structure: + - name: ACK + frombit: 24 + desc: Alert Acknowleged + codes: + 0: Alert acknowledged + 1: Alert not acknowledged + - name: SVR + frombit: 23 + tobit: 22 + desc: Alert Severity + codes: + 0: End fo alert + 1: Pre-alarm + 2: Severe alert +# - name: spare +# frombit: 21 +# tobit: 17 +# desc: Spare bit(s) set to 0 + - name: AT + frombit: 16 + tobit: 9 + desc: Alert Type + - name: AN + frombit: 8 + tobit: 1 + desc: Alert Number + - itemno: I011/605 + itemdesc: Tracks in Alert + frn: 26 + definition: "List of track numbers of the targets concerned by the alert described in I011/600." + format: repetitive + octet: 2 + structure: + - name: Rep + frombit: 24 + tobit: 17 + desc: Repetition Factor +# - name: spare +# frombit: 16 +# tobit: 13 +# desc: Spare bit(s) set to 0 + - name: FTN + frombit: 12 + tobit: 1 + desc: Fusion Track Number + - itemno: I011/610 + itemdesc: Holdbar Status + frn: 27 + definition: "Status of up to sixteen banks of twelve indicators." + format: repetitive + octet: 2 + structure: + - name: Rep + frombit: 24 + tobit: 17 + desc: Repetition Factor + - name: BKN + frombit: 16 + tobit: 13 + desc: Bank Number + - name: I1 + frombit: 12 + desc: Indicator 1 + codes: + 0: Indicator on + 1: Indicator off + - name: I2 + frombit: 11 + desc: Indicator 2 + codes: + 0: Indicator on + 1: Indicator off + - name: I3 + frombit: 10 + desc: Indicator 3 + codes: + 0: Indicator on + 1: Indicator off + - name: I4 + frombit: 9 + desc: Indicator 4 + codes: + 0: Indicator on + 1: Indicator off + - name: I5 + frombit: 8 + desc: Indicator 5 + codes: + 0: Indicator on + 1: Indicator off + - name: I6 + frombit: 7 + desc: Indicator 6 + codes: + 0: Indicator on + 1: Indicator off + - name: I7 + frombit: 6 + desc: Indicator 7 + codes: + 0: Indicator on + 1: Indicator off + - name: I8 + frombit: 5 + desc: Indicator 8 + codes: + 0: Indicator on + 1: Indicator off + - name: I9 + frombit: 4 + desc: Indicator 9 + codes: + 0: Indicator on + 1: Indicator off + - name: I10 + frombit: 3 + desc: Indicator 10 + codes: + 0: Indicator on + 1: Indicator off + - name: I11 + frombit: 2 + desc: Indicator 11 + codes: + 0: Indicator on + 1: Indicator off + - name: I12 + frombit: 1 + desc: Indicator 12 + codes: + 0: Indicator on + 1: Indicator off + - itemno: SPF + itemdesc: Special Purpose Field + frn: 28 + definition: Special Purpose Field. + format: fixed + octet: 1 + structure: + - name: SPF + frombit: 8 + tobit: 1 + desc: SPF + - itemno: RE + itemdesc: Reserved Expansion Field + frn: 29 + definition: Reserved Expansion Field + format: repetitive + octet: 1 + structure: + - name: Rep + frombit: 8 + tobit: 1 + desc: SPF \ No newline at end of file