package com.tsi.comm.server.protocol; import com.tsi.comm.server.dto.TsiCvimAbnormal; import com.tsi.comm.server.dto.TsiCvimControl; import com.tsi.comm.server.dto.TsiCvimStatus; import com.tsi.comm.server.protocol.enums.eLightsStatus; import com.tsi.comm.server.protocol.enums.eLightsType; import com.tsi.comm.server.protocol.enums.eOpCode; import com.tsi.comm.server.protocol.enums.eTimeReliability; import com.tsi.comm.server.xnet.NettyUtils; import com.tsi.comm.server.dto.mongodb.TcsNodeStatus; import com.tsi.comm.server.repository.TsiNodeAddManager; import com.tsi.comm.server.vo.TsiNodeAddDetailVo; import com.tsi.comm.server.vo.TsiNodeAddVo; import com.tsi.comm.server.vo.TsiNodeVo; import com.tsi.common.spring.SpringUtils; import com.tsi.common.utils.ByteUtils; import com.tsi.common.utils.CRC16Utils; import io.netty.channel.Channel; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.text.SimpleDateFormat; import java.util.*; @Slf4j @Getter @Setter public class TsiCpuPacket extends AbstractTsiPacket { // 헤더(10), 상태헤더(8), 상태정보(5)*상태정보건수, 체크섬(2) // CVIB Protocol Format(CPU-->CENTER) //TYPE STX1 STX2 LEN OPCODE DataVer NodeID DATA CHKSUM //Size Byte Byte 2Byte Byte 1Byte 4Byte NByte 2Byte //Value 0x7E 0x7E Size(LEN…CHKSUM) 0x13 0x01 … CRC16 //- LEN : LEN∼CHKSUM data length // - OPCODE: 0x13, 신호상태정보전송 // - NodeID: Node ID //- DATA : Opcode에 따른 데이터 영역 //- CRC16 : CCITT / ITU / CRC - 16, bits shift right, final little(x) big(o) endian encoding.from LEN to DATA /* // 10 byte uint8_t stx1; // stx 0x7E uint8_t stx2; // stx 0x7E uint8_t length[2]; // length, length byte ~ check sum uint8_t opcode; // op code, 0x13 uint8_t dataVer; // Data Version uint8_t nodeid[4]; // signal node id // 8 byte tsc_cvim_hdr_t hdr; // 8 byte, cvim signal status header // 5 byte * count tsc_cvim_stts_t stts[MAX_CVIM_STTS]; // 5 byte * 127 = 635 byte */ // cvim-raw packet header /* #define TIMESPEC_SIZE (sizeof(struct timespec)) // 16 bytes uint8_t pktTm[TIMESPEC_SIZE]; // little endian uint8_t ipaddr[4]; // big endian uint8_t port[2]; // big endian uint8_t connect; uint8_t nodeid[4]; // big endian */ //typedef struct _tsc_cvim_hdr_s //{ // uint8_t manual : 1; /* 수동, 1: 이상, 0: 정상 */ // uint8_t blink : 1; /* 점멸, 1: 점멸, 0: 정상 */ // uint8_t turnOff : 1; /* 소등, 1: 소등, 0: 정상 */ // uint8_t response : 1; /* 감응, 1: 감응, 0: 정상 */ // uint8_t trans : 1; /* 전이, 1: 전이중, 0: 전이완료 */ // uint8_t byte1Res0 : 3; /* 예약, bit 7 ~ 5 */ // // uint8_t conflict : 1; /* 모순 이상, 1 : 이상, 0 : 정상 */ // uint8_t centerComm : 1; /* 센터 통신 이상, 1: 센터 통신이상, 0 : 정상 */ // uint8_t scuComm : 1; /* SCU 통신 이상, 1: MCU <--> SCU 통신 이상, 0: 정상 */ // uint8_t byte2Res0 : 5; /* 예약, bit 7 ~ 3 */ // // uint8_t cycleCounter; /* 주기 카운터, 초 */ // // uint8_t sttsCount : 7; /* 총 신호상태정보의 개수, N 개 */ // uint8_t splitFlag : 1; /* 데이터를 분할하여 전송할 경우 마지막 정보임을 나타내는 플래그(분할 패킷의 처음과 중간:0, 단일 또는 마지막 패킷:1) */ // // uint8_t currTime[4]; /* 현재시간, time_t형, Big Endian */ //} tsc_cvim_hdr_t, *ptsc_cvim_hdr_t; // //typedef struct _tsc_cvim_stts_s //{ // uint8_t dirAdd : 4; /* 3 ~ 0, 방향추가정보, 해당 방향에 연등지 없음(0), 해당 방향의 첫번째 연등지(1), 해당 방향의 두번째 연등지(2) */ // uint8_t lightInfo : 4; /* 7 ~ 4, 신호등 정보, ■ 미지정(0), 직진(1), 좌회전(2), 보행자(3), 자전거(4), 우회전(5), 버스(6), 유턴(7) */ // // uint8_t lighting : 3; /* 2 ~ 0, 신호등 상태, ■ 소등(0), 적색점등(1), 황색점등(2), 녹색점등(3), 적색점멸(4), 황색점멸(5), 녹색점멸(6) */ // uint8_t unprotect : 1; /* 3, 비보호 상태, ■ 신호등 정보 유턴/좌회전에 대한 비보호 여부, ■ 비보호 아님(0), 비보호(1) */ // uint8_t Reserved0 : 2; /* 5 ~ 4, 예비, ■ 예비 */ // uint8_t walkerPush : 1; /* 6, 보행자(푸쉬 또는 자동검지), ■ 없음(0), 버튼 눌림 or 자동검지(1) */ // uint8_t timeFlag : 1; /* 7, 시간 정보 신뢰성, ■ 고정신호시간(0), 가변신호시간(1) */ // // uint8_t displayTm; /* 표출 시간, ■ 초 */ // uint8_t remainTm; /* 잔여 시간, ■ 초 */ // uint8_t dirCode; /* 방향 코드, ■ 출력지정 테이블에 지정된 방향코드 */ //} tsc_cvim_stts_t, *ptsc_cvim_stts_t; // TODO: 20240722 Protocol R28 Added public static final byte PROTOCOL_R27 = 0x01; public static final byte PROTOCOL_R28 = 0x1C; // 28 public static final byte STX1 = 0x7E; public static final byte STX2 = 0x7E; public static final int SIZE_HEAD = 10; public static final int SIZE_STX1 = 1; public static final int SIZE_STX2 = 1; public static final int SIZE_LENGTH = 2; public static final int SIZE_OPCODE = 1; public static final int SIZE_VERSION = 1; public static final int SIZE_NODE_ID = 4; public static final int SIZE_STATUS_HDR = 8; public static final int SIZE_STATUS_DATA = 5; public static final int SIZE_CHECKSUM = 2; public static final int SIZE_PACKET_DATA = 18; // length, opcode, version, node_id, status_hdr, checksum public static final int INDEX_STX1 = 0; // 0 public static final int INDEX_STX2 = 1; // 1 public static final int INDEX_LENGTH = 2; // 2,3 public static final int INDEX_OPCODE = 4; // 4 public static final int INDEX_VERSION = 5; // 5 public static final int INDEX_NODE_ID = 6; // 6,7,8,9 public static final int INDEX_STATUS_HDR = 10; // 10,11,12,13,14,15,16,17 public static final int INDEX_STATUS_DATA = 18; // 18,19,20,21,22 public static final int INDEX_STATUS_DIR_ADD = 0; // 상태정보내 방향 추가 정보 인덱스 public static final int INDEX_STATUS_DIRECTION = 4; // 상태정보내 방향코드 public static final int SIZE_NODE_DUMMY = 1+1+2+1+1+2; // stx1, stx1, length(2), opcode, version, checksum(2) public static final int SIZE_NODE_HEAD = SIZE_NODE_ID + SIZE_STATUS_HDR; public static final int POS_NODE_HEAD_NODEID = 0; // node(4), public static final int POS_NODE_HEAD_COUNT = 7; // node(4), public static final int SIZE_IPC_SIZE = 27; public static final int SIZE_TIMESPEC = 16; public static final int POS_IPC_TIMESPEC = 0; public static final int POS_IPC_IPADDR = 16; public static final int POS_IPC_PORT = 20; public static final int POS_IPC_CONNECT = 22; public static final int POS_IPC_NODEID = 23; public static final int POS_IPC_PACKET = SIZE_IPC_SIZE; public static final byte CONNECT = 0x01; public static final byte DISCONNECT = 0x00; // FOR Netty LengthFieldBasedFrameDecoder public static final int MAX_FRAME_LENGTH = 2048; public static final int LENGTH_FIELD_OFFSET = 2; // 길이 필드가 2번 인덱스에서 시작 public static final int LENGTH_FIELD_LENGTH = 2; // 길이 필드의 크기는 2바이트 public static final int LENGTH_ADJUSTMENT = -2; // 길이 필드 값에 더해야 할 조정값 (길이 필드 자체의 크기를 빼줌) public static final int INITIAL_BYTES_TO_STRIP = 0; // 프레임에서 제거할 초기 바이트 수 private Object obj; private int length; private byte dataVer; private int count; private int checkSum; protected byte[] cvimData; // for cvim-raw topic protected byte[] nodeData; // for node topic protected List addNodes; private final TsiNodeAddManager nodeAddManager; private int calcCheckSum; public TsiCpuPacket(long nodeId, long msec, long nsec, Channel channel) { super(nodeId, msec, nsec, NettyUtils.getRemoteIpAddressToLong(channel), NettyUtils.getRemotePort(channel)); setOpCode(eOpCode.TSI_CPU_SIGNAL_NOTIFY.getValue()); this.nodeAddManager = SpringUtils.getBean(TsiNodeAddManager.class); } public TsiCpuPacket(long nodeId, long msec, long nsec, long remoteIpAddressToLong, int remotePort) { super(nodeId, msec, nsec, remoteIpAddressToLong, remotePort); this.nodeAddManager = SpringUtils.getBean(TsiNodeAddManager.class); } // FOR CVIM packet // public TsiCpuPacket(long nodeId, byte[] value) { // super(nodeId, TimeUtils.currentTimeSeconds(), System.nanoTime(), 0, 0); // this.buf = value; // CVIM Header 를 포함한 내부 IPC 형식 데이터임 // int nodeLength = this.buf.length - TsiCpuPacket.SIZE_IPC_SIZE; // this.cvimData = new byte[TsiCpuPacket.SIZE_IPC_SIZE]; // System.arraycopy(this.buf, 0, this.cvimData, 0, TsiCpuPacket.SIZE_IPC_SIZE); // if (nodeLength > 0) { // this.nodeData = new byte[nodeLength]; // System.arraycopy(this.buf, TsiCpuPacket.SIZE_IPC_SIZE, this.nodeData, 0, nodeLength); // } // } protected byte getStx1() { if (this.buf != null) return this.buf[INDEX_STX1]; return 0x00; } protected byte getStx2() { if (this.buf != null) return this.buf[INDEX_STX2]; return 0x00; } public byte[] getCvimData() { return this.cvimData; } public byte[] getTestData() { return this.buf; } public byte[] getNodeData() { return this.nodeData; } /* * Make cvim-raw packet */ protected void makeCvimPacket() { final int packetLength = (this.buf == null) ? 0 : this.buf.length; this.cvimData = new byte[SIZE_IPC_SIZE + packetLength]; // cvim-raw header System.arraycopy(this.timespec.bytes(), 0, this.cvimData, POS_IPC_TIMESPEC, SIZE_TIMESPEC); ByteUtils.setUnsignedInt(this.cvimData, POS_IPC_IPADDR, getRemoteIp()); ByteUtils.setUnsignedShort(this.cvimData, POS_IPC_PORT, getRemotePort()); this.cvimData[POS_IPC_CONNECT] = (opCode == (byte)eOpCode.TSI_CPU_SIGNAL_NOTIFY.getValue()) ? CONNECT : DISCONNECT; ByteUtils.setUnsignedInt(this.cvimData, POS_IPC_NODEID, this.nodeId); // cvim-raw body if (packetLength > 0) { System.arraycopy(this.buf, 0, this.cvimData, POS_IPC_PACKET, packetLength); } //log.error("CVIM: {}", HexString.fromBytes(this.cvimData)); } /* * Make cvim-raw packet */ protected void makeAddNodeCvimPaket(TsiCpuPacket cpuPacket, byte[] packet) { final int packetLength = (packet == null) ? 0 : packet.length; final int headSize = SIZE_IPC_SIZE + 6; cpuPacket.cvimData = new byte[headSize + packetLength + 2]; // cpu packet 6 byte(nodeid 제외), crc 2 byte // cvim-raw header System.arraycopy(timespec.bytes(), 0, cpuPacket.cvimData, POS_IPC_TIMESPEC, SIZE_TIMESPEC); ByteUtils.setUnsignedInt(cpuPacket.cvimData, POS_IPC_IPADDR, getRemoteIp()); ByteUtils.setUnsignedShort(cpuPacket.cvimData, POS_IPC_PORT, getRemotePort()); cpuPacket.cvimData[POS_IPC_CONNECT] = (opCode == (byte)eOpCode.TSI_CPU_SIGNAL_NOTIFY.getValue()) ? CONNECT : DISCONNECT; ByteUtils.setUnsignedInt(cpuPacket.cvimData, POS_IPC_NODEID, cpuPacket.nodeId); cpuPacket.cvimData[SIZE_IPC_SIZE+INDEX_STX1] = getStx1(); cpuPacket.cvimData[SIZE_IPC_SIZE+INDEX_STX2] = getStx2(); ByteUtils.setUnsignedShort(cpuPacket.cvimData, SIZE_IPC_SIZE+INDEX_LENGTH, packetLength+2); cpuPacket.cvimData[SIZE_IPC_SIZE+INDEX_OPCODE] = (byte)getOpCode(); cpuPacket.cvimData[SIZE_IPC_SIZE+INDEX_VERSION] = getDataVer(); // cvim-raw body if (packetLength > 0) { System.arraycopy(packet, 0, cpuPacket.cvimData, headSize, packetLength); } // 체크섬 계산하지 않음 } protected int checkPacket() { // 0 단계. STX1, STX2 체크 if (this.buf[INDEX_STX1] != STX1 || this.buf[INDEX_STX2] != STX2) { // log.info("Node: {}, STX Error: {}, {}", nodeId, this.buf[INDEX_STX1], this.buf[INDEX_STX2]); // log.error("{}", HexString.fromBytes(this.buf)); return -1; } // 1 단계. 패킷 길이 체크 // TODO: 20240722, Protocol R28 ADDED // int reqLength = SIZE_PACKET_DATA + (SIZE_STATUS_DATA * this.count); // if (this.buf[INDEX_VERSION] == PROTOCOL_R28) { // reqLength = SIZE_PACKET_DATA + (SIZE_STATUS_DATA * this.count) + 1; // ADD CPU byte packet // } // if (this.length != reqLength ) { // log.info("Node: {}, Length Error: {}, Version: {}, status count: {}, {}", nodeId, this.length, this.buf[INDEX_VERSION], // this.count, reqLength); // log.error("{}", HexString.fromBytes(this.buf)); // return false; // } int reqLength = SIZE_PACKET_DATA + (SIZE_STATUS_DATA * this.count); if (this.length < reqLength ) { // log.info("Node: {}, Length Error: {}, Version: {}, status count: {}, {}", nodeId, this.length, this.buf[INDEX_VERSION], // this.count, reqLength); // log.error("{}", HexString.fromBytes(this.buf)); return -2; } // 2단계. 체크섬 this.checkSum = ByteUtils.getUnsignedShort(this.buf, this.buf.length-2); this.calcCheckSum = CRC16Utils.CRC16_ccitt_cvim(this.buf, INDEX_LENGTH, this.length-2); // 시작인덱스가 있으므로 전체길이로 계산 if (this.checkSum != this.calcCheckSum) { // log.error("Node: {}, Check Sum Error: Version: {}, recv: {}, calc: {}", nodeId, this.buf[INDEX_VERSION], this.checkSum, this.calcCheckSum); // log.error("{}", HexString.fromBytes(this.buf)); return -3; } return 0; } public int parsing(TsiNodeVo obj, boolean isCheckPacket) { if (this.buf == null || this.buf.length < INDEX_STATUS_HDR+3) { return -4; } this.opCode = this.buf[INDEX_OPCODE]; this.dataVer = this.buf[INDEX_VERSION]; this.length = ByteUtils.getUnsignedShort(this.buf, INDEX_LENGTH); this.count = (int)(this.buf[INDEX_STATUS_HDR+3] & 0x7F); obj.setSigCount(this.count); // 신호현시 갯수 저장 // CVIM 데이터 및 TEST 데이터가 생성됨 makeCvimPacket(); int result = checkPacket(); if (0 != result) { if (isCheckPacket) { // 20250425: CRC 체크여부에 따라 바로 리턴(기본값은 체크여부가 true 임) return result; } } TsiNodeAddVo tsiNodeAddVo = this.nodeAddManager.get(this.nodeId); if (tsiNodeAddVo == null) { // 연등지 정보가 없는 경우 // R25 인 경우 마지막 CPU 제조사 코드를 복사하지 않도록 수정 int addLength = this.buf.length-SIZE_NODE_DUMMY; final int r25Length = SIZE_NODE_HEAD + (this.count * SIZE_STATUS_DATA); if (addLength > r25Length) { addLength = r25Length; } this.nodeData = new byte[addLength]; System.arraycopy(this.buf, INDEX_NODE_ID, this.nodeData, 0, addLength); return 0; } // 연등지 교차로 데이터 파싱 byte[] head = new byte[SIZE_NODE_HEAD]; List nodeStatus = new ArrayList<>(); Map> addStatus = new HashMap<>(); System.arraycopy(this.buf, INDEX_NODE_ID, head, 0, SIZE_NODE_HEAD); int loop = 0; for (int ii = INDEX_STATUS_DATA; loop < this.count; ii += SIZE_STATUS_DATA, loop++) { byte[] status = new byte[SIZE_STATUS_DATA]; System.arraycopy(this.buf, ii, status, 0, SIZE_STATUS_DATA); int dirAdd = status[INDEX_STATUS_DIR_ADD] & 0x0F; if (dirAdd == 0) { // 원천 노드 정보 nodeStatus.add(status); } else { // 추가 노드 정보 int originDirCode = status[INDEX_STATUS_DIRECTION]; // 원천방향코드 int direction = originDirCode/10; // 인천공항 노드 확인용 int addNodeIdx = originDirCode%10; // 인천공항 노드 확인용 if (addNodeIdx > 4) { // 인천공항 연등지 인 경우 2개 이상이기 때문에 연등지1 = 15, 연등지2=16, .... // 즉, 일의 자리 5,6,7,8,9로 설정하고 1,2,3,4,5로 된다. 십의자리는 *10을 해서 원천방향코드가 된다. dirAdd = addNodeIdx - 4; // 인천공항이 아닌경우는 위에서 구한 dirAdd 을 그대로 사용한다(1,2 만 가능) originDirCode = direction * 10; // 15,16,17,18,19=10, 25,26,27,28,29=20, .... } status[INDEX_STATUS_DIR_ADD] = (byte) (status[INDEX_STATUS_DIR_ADD] & 0xF0); // 방향추가정보를 0 으로 초기화 TsiNodeAddDetailVo detailVo = tsiNodeAddVo.getAddNodeMap().get(originDirCode * 1000 + dirAdd); if (detailVo != null && detailVo.getAddDirCode() != null) { for (int dirIdx = 0; dirIdx < detailVo.getAddDirCode().length; dirIdx++) { // if (detailVo.getAddDirCode()[dirIdx] == (byte)0x00) { // continue; // } // 연등지 방향코드를 배열에 가지고 있기 때문에 List list = addStatus.get(detailVo.getNodeId()); if (list == null) { list = new ArrayList<>(); addStatus.put(detailVo.getNodeId(), list); } if (dirIdx == 0) { status[INDEX_STATUS_DIRECTION] = detailVo.getAddDirCode()[dirIdx]; list.add(status); } else { byte[] status2 = new byte[SIZE_STATUS_DATA]; System.arraycopy(status, 0, status2, 0, SIZE_STATUS_DATA); status2[INDEX_STATUS_DIRECTION] = detailVo.getAddDirCode()[dirIdx]; list.add(status2); } } } } } byte splitFlag = 0x01; int statusCount = nodeStatus.size(); this.nodeData = new byte[SIZE_NODE_HEAD + (statusCount*SIZE_STATUS_DATA)]; System.arraycopy(head, 0, this.nodeData, 0, SIZE_NODE_HEAD); this.nodeData[POS_NODE_HEAD_COUNT] = (byte) (statusCount | (splitFlag << 7)); for (int ii = 0; ii < statusCount; ii++) { System.arraycopy(nodeStatus.get(ii), 0, this.nodeData, SIZE_NODE_HEAD+(ii*SIZE_STATUS_DATA), SIZE_STATUS_DATA); } makeAddNodeCvimPaket(this, this.nodeData); if (addStatus.isEmpty()) { // 연등지 정보가 설정된게 없을까??? return 0; } // 연등지 노드 카프카 패킷 생성 this.addNodes = new ArrayList<>(); for (Map.Entry> entry: addStatus.entrySet()) { statusCount = entry.getValue().size(); TsiCpuAddPacket addPacket = new TsiCpuAddPacket(entry.getKey(), this.timespec, this.remoteIp, this.remotePort); addPacket.setObj(this.nodeAddManager.get(entry.getKey())); addPacket.setNodeData(new byte[SIZE_NODE_HEAD + (statusCount*SIZE_STATUS_DATA)]); System.arraycopy(head, 0, addPacket.getNodeData(), 0, SIZE_NODE_HEAD); ByteUtils.setUnsignedInt(addPacket.getNodeData(), POS_NODE_HEAD_NODEID, entry.getKey()); addPacket.getNodeData()[POS_NODE_HEAD_COUNT] = (byte) (statusCount | (splitFlag << 7)); for (int ii = 0; ii < statusCount; ii++) { System.arraycopy(entry.getValue().get(ii), 0, addPacket.getNodeData(), SIZE_NODE_HEAD+(ii*SIZE_STATUS_DATA), SIZE_STATUS_DATA); } makeAddNodeCvimPaket(addPacket, addPacket.getNodeData()); addNodes.add(addPacket); } /* log.error(" RAW: {}, {}", this.nodeId, HexString.fromBytes(getBuf())); log.error("NODE: {}, {}", this.nodeId, HexString.fromBytes(getNodeData())); log.error("CVIM: {}, {}", this.nodeId, HexString.fromBytes(getCvimData())); for (int ii = 0; ii < addNodes.size(); ii++) { log.error("NODE: {}, {}", addNodes.get(ii).nodeId, HexString.fromBytes(addNodes.get(ii).getNodeData())); log.error("CVIM: {}, {}", addNodes.get(ii).nodeId, HexString.fromBytes(addNodes.get(ii).getCvimData())); }*/ return 0; } public TcsNodeStatus getNodeStatus() { int nodeLength = this.buf.length - TsiCpuPacket.SIZE_IPC_SIZE; byte[] msec = new byte[8]; byte[] nsec = new byte[8]; System.arraycopy(this.cvimData, 0, msec, 0, 8); System.arraycopy(this.cvimData, 8, nsec, 0, 8); ByteBuffer byteMSec = ByteBuffer.wrap(msec); ByteBuffer byteNSec = ByteBuffer.wrap(nsec); byteMSec.order(ByteOrder.LITTLE_ENDIAN); byteNSec.order(ByteOrder.LITTLE_ENDIAN); long milliSeconds = byteMSec.getLong(); long nanoSeconds = byteNSec.getLong(); this.remoteIp = ByteUtils.getUnsignedInt(this.cvimData, TsiCpuPacket.POS_IPC_IPADDR); this.remotePort = ByteUtils.getUnsignedShort(this.cvimData, TsiCpuPacket.POS_IPC_PORT); ///// getTimespec().tv_nsec(nanoSeconds); // recv tcp, Date date = new Date(milliSeconds * 1000L); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); sdf.setTimeZone(java.util.TimeZone.getTimeZone("GMT+9")); String collectTime = sdf.format(date); if (this.cvimData[TsiCpuPacket.POS_IPC_CONNECT] == TsiCpuPacket.DISCONNECT) { return TcsNodeStatus.builder() ._id(this.nodeId) .nodeId(this.nodeId) .isConnect(false) .collectTime(collectTime) .build(); } if (nodeLength < TsiCpuPacket.SIZE_HEAD + TsiCpuPacket.SIZE_STATUS_HDR) { // packet error return null; } String tscDateTime; int cycleElapsedTime; TsiCvimControl tscControlInfo = new TsiCvimControl(); TsiCvimAbnormal tscAbnormalInfo = new TsiCvimAbnormal(); int signalStatusInfoCount; int divFlag; long localTime; List signalStatusInfos = new ArrayList<>(); //this.opCode = this.nodeData[TsiCpuPacket.INDEX_OPCODE]; //byte dataVer = this.nodeData[TsiCpuPacket.INDEX_VERSION]; //int length = ByteUtils.getUnsignedShort(this.nodeData, TsiCpuPacket.INDEX_LENGTH); //int count = (int)(this.nodeData[TsiCpuPacket.INDEX_STATUS_HDR+3] & 0x7F); byte control = this.cvimData[TsiCpuPacket.SIZE_IPC_SIZE+TsiCpuPacket.INDEX_STATUS_HDR+0]; tscControlInfo.inManualControl = ((control ) & 0x01) == 0x01; //수동; tscControlInfo.inFlashingControl = ((control >> 1) & 0x01) == 0x01; //점멸; tscControlInfo.inLightsOutControl = ((control >> 2) & 0x01) == 0x01; //소등 tscControlInfo.inActuationControl = ((control >> 3) & 0x01) == 0x01; //감응; tscControlInfo.inTransitionControl = ((control >> 4) & 0x01) == 0x01; //전이; byte abnormal = this.cvimData[TsiCpuPacket.SIZE_IPC_SIZE+TsiCpuPacket.INDEX_STATUS_HDR+1]; tscAbnormalInfo.inSignalConflict = ((abnormal ) & 0x01) == 0x01; //모순상태; tscAbnormalInfo.inCenterComm = ((abnormal >> 1) & 0x01) == 0x01; //센터상태; tscAbnormalInfo.inScuComm = ((abnormal >> 2) & 0x01) == 0x01; //SCU 상태 cycleElapsedTime = (int)(this.cvimData[TsiCpuPacket.SIZE_IPC_SIZE+TsiCpuPacket.INDEX_STATUS_HDR+2] & 0xFF); byte stts = this.cvimData[TsiCpuPacket.SIZE_IPC_SIZE+TsiCpuPacket.INDEX_STATUS_HDR+3];; signalStatusInfoCount = (int)(stts & 0x7F); //divFlag = (int)((stts >> 7) & 0x01); localTime = ByteUtils.getUnsignedInt(this.cvimData, TsiCpuPacket.SIZE_IPC_SIZE+TsiCpuPacket.INDEX_STATUS_HDR+4); int ii = TsiCpuPacket.SIZE_IPC_SIZE+TsiCpuPacket.INDEX_STATUS_DATA; for (int idx = 0; idx < signalStatusInfoCount; idx++) { TsiCvimStatus status = new TsiCvimStatus(); //int dirAdd = (buffer.get(ii)) & 0x0F; //연등지 int lightsType = (int)((this.cvimData[ii ] >> 4) & 0x0F); int lightsStatus = (int)((this.cvimData[ii+1] ) & 0x07); int reliability = (int)((this.cvimData[ii+1] >> 7) & 0x01); boolean readyPedestrian = ((this.cvimData[ii+1] >> 6) & 0x01) == 0x01; boolean unProtected = ((this.cvimData[ii+1] >> 3) & 0x01) == 0x01; int totalSeconds = (int)(this.cvimData[ii+2] & 0xFF); int remainSeconds = (int)(this.cvimData[ii+3] & 0xFF); int directionCode = (int)(this.cvimData[ii+4] & 0xFF); status.setLightsType(eLightsType.getByValue(lightsType)); // 신호등정보 [직진,좌,보행] status.setLightsStatus(eLightsStatus.getByValue(lightsStatus)); //신호등상태 status.setTimeReliability(eTimeReliability.getByValue(reliability)); //시간정보신뢰성 status.setReadyPedestrianSignal(readyPedestrian); //보행자 status.setUnProtectedSignal(unProtected); //비보호 상태 status.setTotalSeconds(totalSeconds); //표출시간 status.setRemainingSeconds(remainSeconds); //잔여시간 status.setDirectionCode(directionCode); //방향코드 signalStatusInfos.add(status); ii += TsiCpuPacket.SIZE_STATUS_DATA; } date = new Date(localTime * 1000L); sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); sdf.setTimeZone(java.util.TimeZone.getTimeZone("GMT+9")); tscDateTime = sdf.format(date); //log.error("Packet Received: NodeId: {}, {}", this.nodeId, TimeUtils.elapsedTimeStr(System.nanoTime()-nanoSeconds)); return TcsNodeStatus.builder() ._id(this.nodeId) .nodeId(this.nodeId) .isConnect(true) .collectTime(collectTime) .tscDateTime(tscDateTime) .cycleElapsedTime(cycleElapsedTime) .tscControlInfo(tscControlInfo) .tscAbnormalInfo(tscAbnormalInfo) .signalStatusInfoCount(signalStatusInfoCount) .signalStatusInfos(signalStatusInfos) .build(); } }