|
@@ -0,0 +1,343 @@
|
|
|
+package com.sig.todp.server.dto;
|
|
|
+
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+public class TodUtils {
|
|
|
+
|
|
|
+ /*
|
|
|
+ ***************************************************************************************************
|
|
|
+ * FUNCTION NAME : CheckDirection
|
|
|
+ * DESCRIPTION : 전이 방향 옵션에 따라 이번에 전이가 완료되는지를 검사, 1이 리턴되면 이번에 전이 완료,
|
|
|
+ * NOTES :
|
|
|
+ ***************************************************************************************************/
|
|
|
+ public static boolean checkDirection(int dir, int transCycle, int tCycle) {
|
|
|
+ if (dir == TTod.NEGATIVE) {
|
|
|
+ // 음의 방향 전이인 경우, trans Length 가 83%와 100% 사이 이면 전이 완료
|
|
|
+ if (transCycle >= (tCycle*0.83) && transCycle <= tCycle)
|
|
|
+ return true;
|
|
|
+ else
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // 양의 방향 전이인 겨우, transLength 100~134 사이 이면 이전에 전이 완료
|
|
|
+ if (transCycle >= tCycle && transCycle <= (tCycle*1.34))
|
|
|
+ return true;
|
|
|
+ else
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ ***************************************************************************************************
|
|
|
+ * FUNCTION NAME : StretchSplit
|
|
|
+ * DESCRIPTION : 전이중 임시주기 길이를 TOD대로 늘려 맞춤
|
|
|
+ * NOTES :
|
|
|
+ * pt : 전이계획을 저장할 메모리
|
|
|
+ * transCycle : 전이 임시 주기길이
|
|
|
+ * tCycle : 목표 주기 길이
|
|
|
+ * trn : 목표 주기에 대한 TOD 정보 구조체
|
|
|
+ * 반환값: 정상: 1,
|
|
|
+ * 에러: -1 (목표주기가 0값임, Division by Zero)
|
|
|
+ ***************************************************************************************************/
|
|
|
+ public static int stretchSplit(TTod pt, int transCycle, int tCycle, TTransition trn) {
|
|
|
+ /* 주현시가 1현시가 아니고, 선행현시 길이 고정이면, 미리 할당 후 */
|
|
|
+ int transLen[] = new int[TTod.MAX_RINGS]; /* 각 링별 주기길이 */
|
|
|
+ int tCycleLen[] = new int[TTod.MAX_RINGS]; /* 각 링별 목표 주기길이 */
|
|
|
+ int sum[] = new int[TTod.MAX_RINGS]; /* 스플릿팅 한 후 계산된 현시의 합 */
|
|
|
+ int startPhase = 0; /* 고정할당 빼고 자동맞춤 시작 현시 */
|
|
|
+ int ring; /* 링 카운터 변수 */
|
|
|
+ int phase; /* 현시인덱스 카운터변수 */
|
|
|
+ int marginTime; /* 현시합(sum)과 transeCycle와의 편차 */
|
|
|
+ int diff; /* A링 B링 현시 배리어 시간차 */
|
|
|
+ int comp;
|
|
|
+
|
|
|
+ if (tCycle <= 0) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 전이/목표 주기값을 각 링별 주기값으로 적용 */
|
|
|
+ transLen[TTod.ARING] = transCycle;
|
|
|
+ transLen[TTod.BRING] = transCycle;
|
|
|
+ tCycleLen[TTod.ARING] = tCycle;
|
|
|
+ tCycleLen[TTod.BRING] = tCycle;
|
|
|
+
|
|
|
+ /* 현시 셀 제로 초기화(주의!! SetPhase 매크로는 8현시 고정) */
|
|
|
+ pt.setPhase(TTod.ARING, 0);
|
|
|
+ pt.setPhase(TTod.BRING, 0);
|
|
|
+
|
|
|
+ /* 전이중 연동 확보를 위해 주현시 앞 현시들을 계획값으로 고정하는 경우 */
|
|
|
+ if (trn.keepPreInterval == 1 && trn.mainPhaseInx > 0) {
|
|
|
+ for (ring = TTod.ARING; ring <= TTod.BRING; ring++) {
|
|
|
+ for (phase = 0; phase < trn.mainPhaseInx; phase++) {
|
|
|
+ pt.S[ring][phase] = trn.tPlan.S[ring][phase]; /* 선행 고정현시 할당 */
|
|
|
+ transLen[ring] -= pt.S[ring][phase]; /* 임시주기에서 고정현시제외 */
|
|
|
+ tCycleLen[ring] -= pt.S[ring][phase]; /* 목표주기에서도 제외 */
|
|
|
+ }
|
|
|
+ }
|
|
|
+ startPhase = trn.mainPhaseInx;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 나머지 현시들을 계획 현시 비율료 늘려 맞춤 */
|
|
|
+ for (ring = TTod.ARING; ring <= TTod.BRING; ring++) {
|
|
|
+ sum[ring] = 0;
|
|
|
+
|
|
|
+ /* Splitting intervals */
|
|
|
+ for (phase = startPhase; phase < trn.phaseCnt; phase++) {
|
|
|
+ /* 계획 현시와 주기값 비율로 전이주기량을 나누어 줌 */
|
|
|
+ pt.S[ring][phase] = (int) (transLen[ring] * (trn.tPlan.S[ring][phase]/(float)tCycleLen[ring])+0.5);
|
|
|
+
|
|
|
+ /* 최소청시간 위배 체크 */
|
|
|
+ if (pt.S[ring][phase] < trn.minG[ring][phase]) {
|
|
|
+ pt.S[ring][phase] = trn.minG[ring][phase];
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 최대청시간 위배 체크 */
|
|
|
+ if (pt.S[ring][phase] > trn.maxG[ring][phase]) {
|
|
|
+ pt.S[ring][phase] = trn.maxG[ring][phase];
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 최종할당 현시시간의 합 계산 */
|
|
|
+ sum[ring] += pt.S[ring][phase];
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 짜투리 시간 계산 */
|
|
|
+ marginTime = sum[ring]-transLen[ring];
|
|
|
+
|
|
|
+ if (marginTime > 0) {
|
|
|
+ /* 짜투리 시간이 남으면 마지막 현시에서 제거 */
|
|
|
+ pt.S[ring][trn.phaseCnt-1] -= marginTime;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ /* 짜투리 시간이 모자라면 주현시에 할당 */
|
|
|
+ pt.S[ring][trn.mainPhaseInx] += -marginTime;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 이후 현시값을 0값으로 처리 */
|
|
|
+ for (; phase < TTod.MAX_PHASES; phase++) {
|
|
|
+ pt.S[ring][phase] = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 듀얼현시지정 및 최소동시신호시간 검사 */
|
|
|
+ sum[TTod.ARING] = 0;
|
|
|
+ sum[TTod.BRING] = 0;
|
|
|
+
|
|
|
+// trn.dual |= 0x01 < (trn.phaseCnt-1); /* 마지막 현시는 강제로 싱글배리어로 처리 */
|
|
|
+ trn.dual |= (byte) (0x01 << (trn.phaseCnt-1)); /* 마지막 현시는 강제로 싱글배리어로 처리 */
|
|
|
+ for (phase = 0; phase < trn.phaseCnt; phase++) {
|
|
|
+ sum[TTod.ARING] += pt.S[TTod.ARING][phase];
|
|
|
+ sum[TTod.BRING] += pt.S[TTod.BRING][phase];
|
|
|
+
|
|
|
+ /* A링과 B링의 배리어 시간 차를 절대값 으로 구함 */
|
|
|
+ diff = sum[TTod.ARING] - sum[TTod.BRING];
|
|
|
+ if (diff < 0) {
|
|
|
+ diff *= -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 듀얼이 허용되고 최소동시시간 조건이 만족하면 다음현시검사로 진행 */
|
|
|
+ comp = trn.dual & (byte)(0x01 << phase);
|
|
|
+ if (comp != 0 && diff >= trn.minimumOverlap) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ /* 싱글현시이고 현시가 동시에 끝나면 다음현시검사로 진행 */
|
|
|
+ if (sum[TTod.ARING] == sum[TTod.BRING]) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 동시신호조건이 안되거나 싱글인데 다르게 끝나는경우, 강제 조정 */
|
|
|
+ diff = (int) (0.5 + diff / 2.0); /* 시간 차의 반을 구함 */
|
|
|
+ if (sum[TTod.ARING] < sum[TTod.BRING]) {
|
|
|
+ /* A링이 빠르면 A링반늘리고, B링 반줄임 */
|
|
|
+ pt.S[TTod.ARING][phase] += diff; sum[TTod.ARING] += diff;
|
|
|
+ pt.S[TTod.BRING][phase] -= diff; sum[TTod.BRING] -= diff;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ /* 반대면, A링 반줄이고 B링 반늘림 */
|
|
|
+ pt.S[TTod.ARING][phase] -= diff; sum[TTod.ARING] -= diff;
|
|
|
+ pt.S[TTod.BRING][phase] += diff; sum[TTod.BRING] += diff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 합 검사 후 편차만큼 주현시에 인가(주의!! SetPhase 매크로는 8현시 고정) */
|
|
|
+ pt.S[TTod.ARING][trn.mainPhaseInx] += transCycle - pt.sumPhase(TTod.ARING);
|
|
|
+ pt.S[TTod.BRING][trn.mainPhaseInx] += transCycle - pt.sumPhase(TTod.BRING);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ ***************************************************************************************************
|
|
|
+ * FUNCTION NAME : makeTransition
|
|
|
+ * DESCRIPTION : 전이계획 수립 함수
|
|
|
+ * NOTES :
|
|
|
+ * input : 현시 설정 입력데이터 구조체
|
|
|
+ * out : 전이 계획 저장할 tTod[5] 구조체 배열
|
|
|
+ * aclock : 현재 시각(또는 계산 시점의 0:0:0 기준 초단위 누적 시간값)
|
|
|
+ * 반환값: 정상: 0=전이 필요 없음, 1~3 : 전이 횟수,
|
|
|
+ * 에러: -1 : 최대 전이 횟수 초과, -2 : 목표주기가 0값임
|
|
|
+ ***************************************************************************************************/
|
|
|
+ public static int makeTransition(TTransition input, List<TTod> out, int aClock) {
|
|
|
+ int tCycle; /* 전이 목표 주기 길이 */
|
|
|
+ int minCycle; /* 최소주기길이(최소녹색시간의 합) */
|
|
|
+ int maxCycle; /* 최대주기길이(최대녹색시간의 합) */
|
|
|
+ int sumA, sumB; /* 링별 현시합을 계산하여 주기를 계산하기위한 임시 정수 변수 */
|
|
|
+ int r; /* 링을 의미하는 루프계산 카운터 */
|
|
|
+ int p; /* 현시를 의미하는 루프계산 카운터 */
|
|
|
+ int delta ; /* 0시부터 목표주기로 진행되었을 경우 현재 잔여 주기 카운터 */
|
|
|
+ int adjDelta; /* 주현시 개념을 반영하여 보정된 delta */
|
|
|
+ int tCnt; /* 총 전이주기 수를 카운트하기 위한 정수 */
|
|
|
+ int preInterval = 0; /* 주현시 이전 선행현시의 합 */
|
|
|
+ int transLength; /* delta 만큼 진행된 tCycle 의 나머지 시간 */
|
|
|
+
|
|
|
+ int deltaSum;
|
|
|
+ int deltaPerPhase;
|
|
|
+ int transResult;
|
|
|
+
|
|
|
+
|
|
|
+ /* 1. 현재 시각 얻기 */
|
|
|
+ if (aClock == 0) {
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ aClock = now.getHour() * 3600 + now.getMinute() * 60 + now.getSecond();
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 2. 사용하지 않는 현시 자료 초기화 */
|
|
|
+ for (p = input.phaseCnt; p < TTod.MAX_PHASES; p++) {
|
|
|
+ for (r = 0; r < 2; r++) {
|
|
|
+ input.tPlan.S[r][p] = input.minG[r][p] = input.maxG[r][p] = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 3. 목표주기길이 계산(최대값) */
|
|
|
+ sumA = input.tPlan.sumPhase(TTod.ARING);
|
|
|
+ sumB = input.tPlan.sumPhase(TTod.BRING);
|
|
|
+ tCycle = Math.max(sumA, sumB);
|
|
|
+
|
|
|
+ /* 4. 최소주기길이 계산(최대값) */
|
|
|
+ sumA = input.sumPhaseMin(TTod.ARING);
|
|
|
+ sumB = input.sumPhaseMin(TTod.BRING);
|
|
|
+ minCycle = Math.max(sumA, sumB);
|
|
|
+
|
|
|
+ /* 5. 최대주기길이 계산(최대값) */
|
|
|
+ sumA = input.sumPhaseMax(TTod.ARING);
|
|
|
+ sumB = input.sumPhaseMax(TTod.BRING);
|
|
|
+ maxCycle = Math.max(sumA, sumB);
|
|
|
+
|
|
|
+ /* 6. 선행 현시 인터벌 계산 */
|
|
|
+ for (p = 0; p < input.mainPhaseInx; p++) {
|
|
|
+ preInterval += input.tPlan.S[input.progressionRing][p];
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 7. 전이 판단 및 장래 주기 계획 생성 */
|
|
|
+ for (tCnt = 0; tCnt < input.maxTransition && tCnt < TTod.MAX_TRANS; tCnt++) {
|
|
|
+ /* 7-1. delta 계산: 0시|-O-+----C----+dddT ddd= (T-O)%C */
|
|
|
+ delta = (aClock-input.tPlan.O) % tCycle;
|
|
|
+
|
|
|
+ /* 7-2. 주현시 보정 delta 계산 */
|
|
|
+ adjDelta = (delta + preInterval) % tCycle;
|
|
|
+
|
|
|
+ /* 7-3. 첫 전이주기 시작 시각 저장 */
|
|
|
+ out.get(tCnt).hour = aClock/3600;
|
|
|
+ out.get(tCnt).min = (aClock%3600)/60;
|
|
|
+ out.get(tCnt).sec = aClock%60;
|
|
|
+
|
|
|
+ /* 7-4. 전이 여부 결정, 허용 시간(3~5)내 차이는 주현시 단순 보정, 비 전이 처리 */
|
|
|
+ if ( adjDelta <= input.acceptableDelta || /* 0~3초 시간 지난 상황 */
|
|
|
+ (tCycle - adjDelta) <= input.acceptableDelta ) /* 주기에서 0~3초 모자랄때 */
|
|
|
+ {
|
|
|
+ /* 단순보정이므로 TOD 플랜을 일단 복사한 후 보정 */
|
|
|
+ out.get(tCnt).copy(input.tPlan);
|
|
|
+
|
|
|
+ /* 목표 주기 에서 0~3초 모자 라면 음수로 변환 하여 주기를 줄인다. */
|
|
|
+ if ((tCycle - adjDelta) <= input.acceptableDelta) {
|
|
|
+ adjDelta -= tCycle;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 주기 길이 변화가 없으면 전이가 아님 */
|
|
|
+ if (adjDelta == 0) {
|
|
|
+ transResult = tCnt; /* 전이 횟수 */
|
|
|
+ return transResult;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 단순보정으로 줄어든 주기길이가 최소주기보다 길어야 함 */
|
|
|
+ if ((tCycle+adjDelta) >= minCycle && (tCycle+adjDelta) <= maxCycle) {
|
|
|
+ if (adjDelta < 0) {
|
|
|
+ /* 주기가 증가 되는 델타 값은 주 현시에 부여 */
|
|
|
+ out.get(tCnt).S[TTod.ARING][input.mainPhaseInx] -= adjDelta;
|
|
|
+ out.get(tCnt).S[TTod.BRING][input.mainPhaseInx] -= adjDelta;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ /* 감소 델타 값은 타 현시 에서 빼줌 */
|
|
|
+ deltaPerPhase = (int) (0.5 + (float)adjDelta / (input.phaseCnt - 1));
|
|
|
+
|
|
|
+ if (deltaPerPhase < 1) {
|
|
|
+ /* 1초씩도 안 돌아 가면 마지막 현시서 제함 */
|
|
|
+ out.get(tCnt).S[TTod.ARING][input.phaseCnt-1] -= adjDelta;
|
|
|
+ out.get(tCnt).S[TTod.BRING][input.phaseCnt-1] -= adjDelta;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ /* 배분해 주고, 잔차는 마지막 현시 에서 한꺼번에 처리 */
|
|
|
+ for (deltaSum = 0, p = 0; p < input.phaseCnt-1; p++) {
|
|
|
+ if (p==input.mainPhaseInx)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ out.get(tCnt).S[TTod.ARING][p] -= deltaPerPhase;
|
|
|
+ out.get(tCnt).S[TTod.BRING][p] -= deltaPerPhase;
|
|
|
+ deltaSum += deltaPerPhase;
|
|
|
+ }
|
|
|
+
|
|
|
+ out.get(tCnt).S[TTod.ARING][input.phaseCnt-1] -= adjDelta-deltaSum;
|
|
|
+ out.get(tCnt).S[TTod.BRING][input.phaseCnt-1] -= adjDelta-deltaSum;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ transResult = tCnt; /* 전이 횟수 */
|
|
|
+ return transResult;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 10. 이번 전이주기가 너무 작으면 1주기보다 긴 값으로 전이주기 조정 */
|
|
|
+ transLength = tCycle - adjDelta;
|
|
|
+ if (transLength < minCycle)
|
|
|
+ {
|
|
|
+ transLength += tCycle;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 11. transLength가 목표주기의 83% ~ 133% 사이이면 이번에 전이 완료 */
|
|
|
+ if (checkDirection(input.transDir, transLength, tCycle)) {
|
|
|
+ if (stretchSplit(out.get(tCnt), transLength, tCycle, input) < 0) {
|
|
|
+ transResult = TTod.CYCLE_LENGTH_ERR;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ transResult = tCnt + 1; /* 전이 완료 */
|
|
|
+ }
|
|
|
+ return transResult;
|
|
|
+ }
|
|
|
+ /* 12. 전이가 한번에 끝나지 않으면 일단 1회 최대 전이량(Positive 면 133%, Negative 이면-17%)
|
|
|
+ * 전이 주기 저장 후 계산 시각 (aClock)을 그만큼 이동
|
|
|
+ */
|
|
|
+ else {
|
|
|
+ /* 음의 방향 전이 이면 줄여서 전이 */
|
|
|
+ if (input.transDir == TTod.NEGATIVE) {
|
|
|
+ transLength = (int) (tCycle * 0.83);
|
|
|
+ }
|
|
|
+ /* 양의 방향 전이 이면 늘려서 전이 */
|
|
|
+ else {
|
|
|
+ transLength = (int) (tCycle * 1.34);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 현시 맞추어 늘림 */
|
|
|
+ if (stretchSplit(out.get(tCnt), transLength, tCycle, input) < 0) {
|
|
|
+ transResult = TTod.CYCLE_LENGTH_ERR;
|
|
|
+ return transResult;
|
|
|
+ }
|
|
|
+ aClock += transLength;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 허용 전이 횟수 까지 루프가 돌고 break 되면 전이 횟수 초과 에러 */
|
|
|
+ transResult = TTod.EXCEED_MAXTRANS;
|
|
|
+ return transResult;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|