Browse Source

[비행관제] 비정상 상황 작업

feature/auth
노승철 2 years ago
parent
commit
345139f44c
  1. 25
      src/components/map/naver/NaverMap.js
  2. 5
      src/components/map/naver/dron/DronHistory.js
  3. 6
      src/components/map/naver/dron/DronMarker.js
  4. 17
      src/components/map/naver/dron/DronPlan.js
  5. 46
      src/components/map/naver/dron/DronToast.js
  6. 2
      src/components/websocket/WebsocketClient.js
  7. 17
      src/modules/control/gp/actions/controlGpAction.ts
  8. 24
      src/modules/control/gp/apis/controlGpApi.ts
  9. 17
      src/modules/control/gp/models/controlGpModel.ts
  10. 10
      src/modules/control/gp/reducers/controlGpReducer.ts
  11. 54
      src/modules/control/gp/sagas/controlGpSaga.ts

25
src/components/map/naver/NaverMap.js

@ -11,16 +11,21 @@ import geoJson from '../geojson/airArea.json';
import SensorZone from "./sensor/SensorZone";
import { controlGroupAuthAction } from '../../../modules/control/gp';
import DronPlan from './dron/DronPlan';
import DronToast from './dron/DronToast';
import { toast } from 'react-toastify';
export const NaverCustomMap = () => {
const dispatch = useDispatch();
const naver = window.naver;
// const { controlGpContains } = useSelector(state => state.controlGpFltPlanState);
const [isMapLoad, setIsMapLoad] = useState(false);
const [mapObject, setMapObject] = useState(null);
const [arrPolyline, setArrPolyline] = useState([]);
let arrMarkers = []; // 마커 배열
let arrPolyline = []; // 폴리라인 배열
// let arrPolyline = []; // 폴리라인 배열
let features = geoJson.features;
useEffect(() => {
@ -31,6 +36,19 @@ export const NaverCustomMap = () => {
useEffect(() => {
}, [mapObject]);
// useEffect(() => {
// if (controlGpContains) {
// if(!controlGpContains.contains) {
// toast.info(<DronToast title={'비정상 상황 알림'} message={'경로 상에 비행 구역을 이탈하였습니다.'}/>, {
// autoClose: 3000,
// hideProgressBar: true,
// position: toast.POSITION.BOTTOM_RIGHT,
// })
// }
// }
// }, [controlGpContains])
const NaverMapInit = () => {
const mapOptions = {
center: new naver.maps.LatLng(36.56793936069445, 127.85101412107547),
@ -47,6 +65,10 @@ export const NaverCustomMap = () => {
setMapObject(new naver.maps.Map('map', mapOptions));
};
const handleHistoryInit = (line) => {
setArrPolyline([...arrPolyline, line])
}
return (
<>
<div id='map' style={{ width: '100%', height: '100vh' }}></div>
@ -67,6 +89,7 @@ export const NaverCustomMap = () => {
map={mapObject}
naver={naver}
arrPolyline={arrPolyline}
handleHistoryInit={handleHistoryInit}
/>
<FeatureAirZone map={mapObject} naver={naver} features={features} />

5
src/components/map/naver/dron/DronHistory.js

@ -16,7 +16,7 @@ export const DronHistory = props => {
useEffect(() => {
// console.log('>>>', controlGpHistory);
polylineRemove();
// polylineRemove();
polylineInit();
}, [controlGpHistory]);
@ -29,6 +29,7 @@ export const DronHistory = props => {
}, [objectId, isClickObject]);
const polylineRemove = () => {
console.log(props.arrPolyline);
if (props.arrPolyline) {
props.arrPolyline.map(item => {
item.setMap(null);
@ -55,7 +56,7 @@ export const DronHistory = props => {
polyline.setPath(polylinePath);
polyline.setMap(props.map);
props.arrPolyline.push(polyline);
props.handleHistoryInit(polyline);
}
};

6
src/components/map/naver/dron/DronMarker.js

@ -87,13 +87,13 @@ export const DronMarker = props => {
const idntfNum = marker.id;
const contorlId = marker.controlId;
// 클릭한 식별번호 정보를 가진 그룹 추출 (1건만 존재해야하는게 맞는 듯)
const group = controlGroupAuthInfo.find(group => group.idntfNum === idntfNum);
// 클릭한 식별번호 정보를 가진 그룹 추출
// const group = controlGroupAuthInfo.find(group => group.idntfNum === idntfNum);
//히스토리 불러오기
dispatch(objectClickAction(contorlId));
dispatch(controlGpDtlAction.request(contorlId));
dispatch(controlGpFlightPlanAction.request(group));
dispatch(controlGpFlightPlanAction.request(idntfNum));
};
//마커를 삭제 한다.

17
src/components/map/naver/dron/DronPlan.js

@ -1,6 +1,8 @@
import { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { controlGpFlightPlanInitAction } from '../../../../modules/control/gp';
import DronToast from './DronToast';
import { toast } from 'react-toastify';
const DronPlan = ({ naver, map }) => {
@ -9,6 +11,8 @@ const DronPlan = ({ naver, map }) => {
const { controlGpList } = useSelector(state => state.controlGpState);
const { controlGpFltPlanList } = useSelector(state => state.controlGpFltPlanState);
const { objectId, isClickObject } = useSelector(state => state.controlMapReducer);
const { controlGpContains } = useSelector(state => state.controlGpFltPlanState);
const [area, setArea] = useState(); // 비행 구역 관리
const [buffer, setBuffer] = useState(); // 버퍼 구역 관리
@ -45,6 +49,19 @@ const DronPlan = ({ naver, map }) => {
}
}, [controlGpList])
useEffect(() => {
if (controlGpContains) {
if(!controlGpContains.contains) {
toast.info(<DronToast title={'비정상 상황 알림'} message={'경로 상에 비행 구역을 이탈하였습니다.'}/>, {
autoClose: 3000,
hideProgressBar: true,
position: toast.POSITION.BOTTOM_RIGHT,
})
}
}
}, [controlGpContains])
/* 비행 구역 그리기. */
const init = () => {

46
src/components/map/naver/dron/DronToast.js

@ -0,0 +1,46 @@
import React, { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Fragment } from 'react';
import { toast } from 'react-toastify';
import Avatar from '../../../../@core/components/avatar';
import { Bell, Check, X, AlertTriangle, Info } from 'react-feather'
import {
Card,
CardHeader,
CardBody,
CardTitle,
Button,
Toast,
ToastBody,
ToastHeader,
Row,
Col,
} from 'reactstrap';
import log from '../../../../assets/images/logo/logo.png';
const DronToast = ({ title, message }) => {
return (
<Fragment>
<div className='toastify-header'>
<div className='title-wrapper'>
<Avatar size='sm' color='info' icon={<Info size={12} />} />
<h6 className='text-info ml-50 mb-0'>
{title}
</h6>
</div>
</div>
<div className='toastify-body'>
<span>
{message}
</span>
</div>
</Fragment>
)
}
export default DronToast;

2
src/components/websocket/WebsocketClient.js

@ -26,6 +26,8 @@ const WebsocketClient = () => {
const data = e.data;
const controlGpList = JSON.parse(data);
// console.log('gps ', controlGpList)
dispatch(Actions.controlGpAction.request(controlGpList));
};

17
src/modules/control/gp/actions/controlGpAction.ts

@ -6,8 +6,9 @@ import {
ControlGpData,
ControlGpDtlState,
ControlGpFlightPlanDataList,
ControlGpFlightPlanRQ,
ControlGpHisState,
ControlGpPlanContainsRQ,
ControlGpPlanContainsRS,
ControlGpState,
ControlGroupAuthState
} from '../models/controlGpModel';
@ -40,6 +41,11 @@ const CONTROL_FLIGHT_PLAN_FAILURE = 'control/gp/flight/plan/CONTROL_FLIGHT_PLAN_
const CONTROL_FLIGHT_PLAN_INIT = 'control/gp/flight/plan/CONTROL_FLIGHT_PLAN_INIT';
// [관제] 비행 구역 비정상 여부 체크
const CONTROL_FLIGHT_PLAN_WARN_REQUEST = 'control/gp/flight/plan/CONTROL_FLIGHT_PLAN_WARN_REQUEST';
const CONTROL_FLIGHT_PLAN_WARN_SUCCESS = 'control/gp/flight/plan/CONTROL_FLIGHT_PLAN_WARN_SUCCESS';
const CONTROL_FLIGHT_PLAN_WARN_FAILURE = 'control/gp/flight/plan/CONTROL_FLIGHT_PLAN_WARN_FAILURE';
export const controlGpAction = createAsyncAction(
CONTROL_GP_REQUEST,
CONTROL_GP_SUCCESS,
@ -74,7 +80,13 @@ export const controlGpFlightPlanAction = createAsyncAction(
CONTROL_FLIGHT_PLAN_REQUEST,
CONTROL_FLIGHT_PLAN_SUCCESS,
CONTROL_FLIGHT_PLAN_FAILURE,
)<ControlGpFlightPlanRQ, ControlGpFlightPlanDataList, AxiosError>();
)<string, ControlGpFlightPlanDataList, AxiosError>();
export const controlGpFlightPlanWarnAction = createAsyncAction(
CONTROL_FLIGHT_PLAN_WARN_REQUEST,
CONTROL_FLIGHT_PLAN_WARN_SUCCESS,
CONTROL_FLIGHT_PLAN_WARN_FAILURE,
)<ControlGpPlanContainsRQ, ControlGpPlanContainsRS, AxiosError>();
export const controlGpFlightPlanInitAction = createAction(CONTROL_FLIGHT_PLAN_INIT)();
@ -85,6 +97,7 @@ const actions = {
controlGpRtDtlAction,
controlGroupAuthAction,
controlGpFlightPlanAction,
controlGpFlightPlanWarnAction,
controlGpFlightPlanInitAction
};

24
src/modules/control/gp/apis/controlGpApi.ts

@ -1,5 +1,5 @@
import axios from '../../../utils/customAxiosUtil';
import { ReponseControlGpHistory, ControlGroupAuthData, ResponseControlGroupAuth, ControlGpFlightPlanRQ } from '../models/controlGpModel';
import { ReponseControlGpHistory, ControlGroupAuthData, ResponseControlGroupAuth, ControlGpPlanContainsRQ} from '../models/controlGpModel';
import qs from 'qs';
export const controlGpApi = {
@ -26,18 +26,24 @@ export const controlGpApi = {
);
return data;
},
getFlightPlan: async (rq: ControlGpFlightPlanRQ) => {
if (!rq.idntfNum) {
getFlightPlan: async (idntfNum: string) => {
if (!idntfNum) {
return null;
}
const queryString = qs.stringify(rq, {
addQueryPrefix: true,
arrayFormat: 'repeat'
});
const { data } = await axios.get(
`api/ctr/cntrl/flight_plan${queryString}`
`api/ctr/cntrl/flight_plan/${idntfNum}`
);
return data;
},
checkPlanContains: async (rq: ControlGpPlanContainsRQ) => {
if(!rq.idntfNum) {
return null;
}
const { data } = await axios.post(
`api/ctr/cntrl/contains`, rq
);
return data;

17
src/modules/control/gp/models/controlGpModel.ts

@ -1,9 +1,12 @@
import { FlightPlanData } from "../../../basis/flight/models/basisFlightModel";
export interface ControlGpState {
controlGpList: ControlGpData[] | undefined;
}
export interface ControlGpFlightPlanState {
controlGpFltPlanList: ControlGpFlightPlanDataList | undefined;
controlGpContains: ControlGpPlanContainsRS | undefined;
}
export interface ControlGpHisState {
@ -13,6 +16,7 @@ export interface ControlGpHisState {
export interface ControlGpDtlState {
controlGpDetail: ControlGpDtlData | undefined;
controlDetail: ControlDetailData | undefined;
}
export interface ControlGroupAuthState {
@ -151,10 +155,16 @@ export interface FlightPlanAreaCoordData {
// docState: string
}
export interface ControlGpFlightPlanRQ {
export interface ControlGpPlanContainsRQ {
idntfNum: string,
lat: number,
lon: number,
planList: FlightPlanData[]
}
export interface ControlGpPlanContainsRS {
contains: boolean,
idntfNum: string,
groupId: string,
cstmrSno: number, // 흠
}
export interface ReponseControlGpHistory {
@ -172,4 +182,5 @@ export const initiaResponseControlGpData = {
controlDetail: undefined,
controlGroupAuthInfo: undefined,
controlGpFltPlanList: undefined,
controlGpContains: undefined
};

10
src/modules/control/gp/reducers/controlGpReducer.ts

@ -6,6 +6,7 @@ import {
controlGpDtlAction,
controlGpFlightPlanAction,
controlGpFlightPlanInitAction,
controlGpFlightPlanWarnAction,
controlGpHisAction,
controlGpRtDtlAction,
controlGroupAuthAction
@ -25,7 +26,6 @@ export const controlGpReducer = createReducer<ControlGpState, ControlGpAction>(
).handleAction(controlGpAction.success, (state, action) =>
produce(state, draft => {
const { controlGpList } = action.payload;
console.log('gps ', controlGpList)
draft.controlGpList = controlGpList;
})
)
@ -43,6 +43,12 @@ export const controlGpFltPlanReducer = createReducer<ControlGpFlightPlanState, C
draft.controlGpFltPlanList = undefined;
})
)
.handleAction(controlGpFlightPlanWarnAction.success, (state, action) =>
produce(state, draft => {
const data = action.payload;
draft.controlGpContains = data;
})
);
export const controlGpHisReducer = createReducer<
@ -74,7 +80,7 @@ export const controlGpDtlReducer = createReducer<
draft.controlDetail = data;
})
);
)
export const controlGroupAuthReducer = createReducer<
ControlGroupAuthState,

54
src/modules/control/gp/sagas/controlGpSaga.ts

@ -15,6 +15,8 @@ function* getControlGpSaga(
const state = yield select();
const { objectId, isClickObject } = state.controlMapReducer;
const { controlGroupAuthInfo } = state.controlGroupAuthState;
const { controlGpFltPlanList } = state.controlGpFltPlanState;
const { controlGpHistory } = state.controlGpHisState;
let gpsData: ControlGpData[] = [];
@ -44,17 +46,39 @@ function* getControlGpSaga(
if (objectId && isClickObject) {
let detailData;
//History 호출
yield put(Actions.controlGpHisAction.request({ id: objectId }));
//상세 정보에서 실시간 데이터 호출
data.map(item => {
gpsData.map(item => {
if (item.controlId === objectId) {
detailData = item;
}
});
yield put(Actions.controlGpRtDtlAction.request(detailData));
// History Push
if(controlGpHistory) {
const stateHistory = controlGpHistory;
const history = stateHistory.map((h) => {
return {
...h,
detailData
}
});
yield put(Actions.controlGpHisAction.success(history));
}
// 비행구역 contains check
if(controlGpFltPlanList) {
const containsRQ = {
idntfNum: detailData.objectId,
lat: detailData.lat,
lon: detailData.lng,
planList: controlGpFltPlanList
}
yield put(Actions.controlGpFlightPlanWarnAction.request(containsRQ));
}
}
} catch (error) {
yield put(Actions.controlGpAction.failure(error));
@ -107,7 +131,7 @@ function* controlDtlSaga(
console.log('data>>>>>>>>>>>>>>>>>>>>>', data);
yield put(Actions.controlGpDtlAction.success(data));
} catch (error) {
yield put(Actions.controlGpRtDtlAction.failure(error));
yield put(Actions.controlGpDtlAction.failure(error));
}
}
@ -135,9 +159,9 @@ function* controlGpFlightPlanSaga (
action: ActionType<typeof Actions.controlGpFlightPlanAction.request>
) {
try {
const rq = action.payload;
const idntfNum = action.payload;
const list = yield call(controlGpApi.getFlightPlan, rq);
const list = yield call(controlGpApi.getFlightPlan, idntfNum);
console.log("flight plan list : ", list);
@ -148,6 +172,21 @@ function* controlGpFlightPlanSaga (
}
}
function* controlGpFlightPlanWarnSaga(
action: ActionType<typeof Actions.controlGpFlightPlanWarnAction.request>
) {
try {
const idntfNum = action.payload;
const rs = yield call(controlGpApi.checkPlanContains, idntfNum);
yield put(Actions.controlGpFlightPlanWarnAction.success(rs));
} catch (error) {
yield put(Actions.controlGpFlightPlanWarnAction.failure(error));
}
}
export function* controlGpSaga() {
yield takeEvery(Actions.controlGpAction.request, getControlGpSaga);
yield takeEvery(Actions.controlGpHisAction.request, getControlGpHistorySaga);
@ -155,4 +194,5 @@ export function* controlGpSaga() {
yield takeEvery(Actions.controlGpDtlAction.request, controlDtlSaga);
yield takeEvery(Actions.controlGroupAuthAction.request, controlGroupAuthSaga);
yield takeEvery(Actions.controlGpFlightPlanAction.request, controlGpFlightPlanSaga);
yield takeEvery(Actions.controlGpFlightPlanWarnAction.request, controlGpFlightPlanWarnSaga);
}

Loading…
Cancel
Save