TsiCpuPacket.java 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. package com.tsi.comm.server.protocol;
  2. import com.tsi.comm.server.dto.TsiCvimAbnormal;
  3. import com.tsi.comm.server.dto.TsiCvimControl;
  4. import com.tsi.comm.server.dto.TsiCvimStatus;
  5. import com.tsi.comm.server.protocol.enums.eLightsStatus;
  6. import com.tsi.comm.server.protocol.enums.eLightsType;
  7. import com.tsi.comm.server.protocol.enums.eOpCode;
  8. import com.tsi.comm.server.protocol.enums.eTimeReliability;
  9. import com.tsi.comm.server.xnet.NettyUtils;
  10. import com.tsi.comm.server.dto.mongodb.TcsNodeStatus;
  11. import com.tsi.comm.server.repository.TsiNodeAddManager;
  12. import com.tsi.comm.server.vo.TsiNodeAddDetailVo;
  13. import com.tsi.comm.server.vo.TsiNodeAddVo;
  14. import com.tsi.comm.server.vo.TsiNodeVo;
  15. import com.tsi.common.spring.SpringUtils;
  16. import com.tsi.common.utils.ByteUtils;
  17. import com.tsi.common.utils.CRC16Utils;
  18. import io.netty.channel.Channel;
  19. import lombok.Getter;
  20. import lombok.Setter;
  21. import lombok.extern.slf4j.Slf4j;
  22. import java.nio.ByteBuffer;
  23. import java.nio.ByteOrder;
  24. import java.text.SimpleDateFormat;
  25. import java.util.*;
  26. @Slf4j
  27. @Getter
  28. @Setter
  29. public class TsiCpuPacket extends AbstractTsiPacket {
  30. // 헤더(10), 상태헤더(8), 상태정보(5)*상태정보건수, 체크섬(2)
  31. // CVIB Protocol Format(CPU-->CENTER)
  32. //TYPE STX1 STX2 LEN OPCODE DataVer NodeID DATA CHKSUM
  33. //Size Byte Byte 2Byte Byte 1Byte 4Byte NByte 2Byte
  34. //Value 0x7E 0x7E Size(LEN…CHKSUM) 0x13 0x01 … CRC16
  35. //- LEN : LEN∼CHKSUM data length
  36. // - OPCODE: 0x13, 신호상태정보전송
  37. // - NodeID: Node ID
  38. //- DATA : Opcode에 따른 데이터 영역
  39. //- CRC16 : CCITT / ITU / CRC - 16, bits shift right, final little(x) big(o) endian encoding.from LEN to DATA
  40. /*
  41. // 10 byte
  42. uint8_t stx1; // stx 0x7E
  43. uint8_t stx2; // stx 0x7E
  44. uint8_t length[2]; // length, length byte ~ check sum
  45. uint8_t opcode; // op code, 0x13
  46. uint8_t dataVer; // Data Version
  47. uint8_t nodeid[4]; // signal node id
  48. // 8 byte
  49. tsc_cvim_hdr_t hdr; // 8 byte, cvim signal status header
  50. // 5 byte * count
  51. tsc_cvim_stts_t stts[MAX_CVIM_STTS]; // 5 byte * 127 = 635 byte
  52. */
  53. // cvim-raw packet header
  54. /*
  55. #define TIMESPEC_SIZE (sizeof(struct timespec)) // 16 bytes
  56. uint8_t pktTm[TIMESPEC_SIZE]; // little endian
  57. uint8_t ipaddr[4]; // big endian
  58. uint8_t port[2]; // big endian
  59. uint8_t connect;
  60. uint8_t nodeid[4]; // big endian
  61. */
  62. //typedef struct _tsc_cvim_hdr_s
  63. //{
  64. // uint8_t manual : 1; /* 수동, 1: 이상, 0: 정상 */
  65. // uint8_t blink : 1; /* 점멸, 1: 점멸, 0: 정상 */
  66. // uint8_t turnOff : 1; /* 소등, 1: 소등, 0: 정상 */
  67. // uint8_t response : 1; /* 감응, 1: 감응, 0: 정상 */
  68. // uint8_t trans : 1; /* 전이, 1: 전이중, 0: 전이완료 */
  69. // uint8_t byte1Res0 : 3; /* 예약, bit 7 ~ 5 */
  70. //
  71. // uint8_t conflict : 1; /* 모순 이상, 1 : 이상, 0 : 정상 */
  72. // uint8_t centerComm : 1; /* 센터 통신 이상, 1: 센터 통신이상, 0 : 정상 */
  73. // uint8_t scuComm : 1; /* SCU 통신 이상, 1: MCU <--> SCU 통신 이상, 0: 정상 */
  74. // uint8_t byte2Res0 : 5; /* 예약, bit 7 ~ 3 */
  75. //
  76. // uint8_t cycleCounter; /* 주기 카운터, 초 */
  77. //
  78. // uint8_t sttsCount : 7; /* 총 신호상태정보의 개수, N 개 */
  79. // uint8_t splitFlag : 1; /* 데이터를 분할하여 전송할 경우 마지막 정보임을 나타내는 플래그(분할 패킷의 처음과 중간:0, 단일 또는 마지막 패킷:1) */
  80. //
  81. // uint8_t currTime[4]; /* 현재시간, time_t형, Big Endian */
  82. //} tsc_cvim_hdr_t, *ptsc_cvim_hdr_t;
  83. //
  84. //typedef struct _tsc_cvim_stts_s
  85. //{
  86. // uint8_t dirAdd : 4; /* 3 ~ 0, 방향추가정보, 해당 방향에 연등지 없음(0), 해당 방향의 첫번째 연등지(1), 해당 방향의 두번째 연등지(2) */
  87. // uint8_t lightInfo : 4; /* 7 ~ 4, 신호등 정보, ■ 미지정(0), 직진(1), 좌회전(2), 보행자(3), 자전거(4), 우회전(5), 버스(6), 유턴(7) */
  88. //
  89. // uint8_t lighting : 3; /* 2 ~ 0, 신호등 상태, ■ 소등(0), 적색점등(1), 황색점등(2), 녹색점등(3), 적색점멸(4), 황색점멸(5), 녹색점멸(6) */
  90. // uint8_t unprotect : 1; /* 3, 비보호 상태, ■ 신호등 정보 유턴/좌회전에 대한 비보호 여부, ■ 비보호 아님(0), 비보호(1) */
  91. // uint8_t Reserved0 : 2; /* 5 ~ 4, 예비, ■ 예비 */
  92. // uint8_t walkerPush : 1; /* 6, 보행자(푸쉬 또는 자동검지), ■ 없음(0), 버튼 눌림 or 자동검지(1) */
  93. // uint8_t timeFlag : 1; /* 7, 시간 정보 신뢰성, ■ 고정신호시간(0), 가변신호시간(1) */
  94. //
  95. // uint8_t displayTm; /* 표출 시간, ■ 초 */
  96. // uint8_t remainTm; /* 잔여 시간, ■ 초 */
  97. // uint8_t dirCode; /* 방향 코드, ■ 출력지정 테이블에 지정된 방향코드 */
  98. //} tsc_cvim_stts_t, *ptsc_cvim_stts_t;
  99. // TODO: 20240722 Protocol R28 Added
  100. public static final byte PROTOCOL_R27 = 0x01;
  101. public static final byte PROTOCOL_R28 = 0x1C; // 28
  102. public static final byte STX1 = 0x7E;
  103. public static final byte STX2 = 0x7E;
  104. public static final int SIZE_HEAD = 10;
  105. public static final int SIZE_STX1 = 1;
  106. public static final int SIZE_STX2 = 1;
  107. public static final int SIZE_LENGTH = 2;
  108. public static final int SIZE_OPCODE = 1;
  109. public static final int SIZE_VERSION = 1;
  110. public static final int SIZE_NODE_ID = 4;
  111. public static final int SIZE_STATUS_HDR = 8;
  112. public static final int SIZE_STATUS_DATA = 5;
  113. public static final int SIZE_CHECKSUM = 2;
  114. public static final int SIZE_PACKET_DATA = 18; // length, opcode, version, node_id, status_hdr, checksum
  115. public static final int INDEX_STX1 = 0; // 0
  116. public static final int INDEX_STX2 = 1; // 1
  117. public static final int INDEX_LENGTH = 2; // 2,3
  118. public static final int INDEX_OPCODE = 4; // 4
  119. public static final int INDEX_VERSION = 5; // 5
  120. public static final int INDEX_NODE_ID = 6; // 6,7,8,9
  121. public static final int INDEX_STATUS_HDR = 10; // 10,11,12,13,14,15,16,17
  122. public static final int INDEX_STATUS_DATA = 18; // 18,19,20,21,22
  123. public static final int INDEX_STATUS_DIR_ADD = 0; // 상태정보내 방향 추가 정보 인덱스
  124. public static final int INDEX_STATUS_DIRECTION = 4; // 상태정보내 방향코드
  125. public static final int SIZE_NODE_DUMMY = 1+1+2+1+1+2; // stx1, stx1, length(2), opcode, version, checksum(2)
  126. public static final int SIZE_NODE_HEAD = SIZE_NODE_ID + SIZE_STATUS_HDR;
  127. public static final int POS_NODE_HEAD_NODEID = 0; // node(4),
  128. public static final int POS_NODE_HEAD_COUNT = 7; // node(4),
  129. public static final int SIZE_IPC_SIZE = 27;
  130. public static final int SIZE_TIMESPEC = 16;
  131. public static final int POS_IPC_TIMESPEC = 0;
  132. public static final int POS_IPC_IPADDR = 16;
  133. public static final int POS_IPC_PORT = 20;
  134. public static final int POS_IPC_CONNECT = 22;
  135. public static final int POS_IPC_NODEID = 23;
  136. public static final int POS_IPC_PACKET = SIZE_IPC_SIZE;
  137. public static final byte CONNECT = 0x01;
  138. public static final byte DISCONNECT = 0x00;
  139. // FOR Netty LengthFieldBasedFrameDecoder
  140. public static final int MAX_FRAME_LENGTH = 2048;
  141. public static final int LENGTH_FIELD_OFFSET = 2; // 길이 필드가 2번 인덱스에서 시작
  142. public static final int LENGTH_FIELD_LENGTH = 2; // 길이 필드의 크기는 2바이트
  143. public static final int LENGTH_ADJUSTMENT = -2; // 길이 필드 값에 더해야 할 조정값 (길이 필드 자체의 크기를 빼줌)
  144. public static final int INITIAL_BYTES_TO_STRIP = 0; // 프레임에서 제거할 초기 바이트 수
  145. private Object obj;
  146. private int length;
  147. private byte dataVer;
  148. private int count;
  149. private int checkSum;
  150. protected byte[] cvimData; // for cvim-raw topic
  151. protected byte[] nodeData; // for node topic
  152. protected List<TsiCpuAddPacket> addNodes;
  153. private final TsiNodeAddManager nodeAddManager;
  154. private int calcCheckSum;
  155. public TsiCpuPacket(long nodeId, long msec, long nsec, Channel channel) {
  156. super(nodeId, msec, nsec, NettyUtils.getRemoteIpAddressToLong(channel), NettyUtils.getRemotePort(channel));
  157. setOpCode(eOpCode.TSI_CPU_SIGNAL_NOTIFY.getValue());
  158. this.nodeAddManager = SpringUtils.getBean(TsiNodeAddManager.class);
  159. }
  160. public TsiCpuPacket(long nodeId, long msec, long nsec, long remoteIpAddressToLong, int remotePort) {
  161. super(nodeId, msec, nsec, remoteIpAddressToLong, remotePort);
  162. this.nodeAddManager = SpringUtils.getBean(TsiNodeAddManager.class);
  163. }
  164. // FOR CVIM packet
  165. // public TsiCpuPacket(long nodeId, byte[] value) {
  166. // super(nodeId, TimeUtils.currentTimeSeconds(), System.nanoTime(), 0, 0);
  167. // this.buf = value; // CVIM Header 를 포함한 내부 IPC 형식 데이터임
  168. // int nodeLength = this.buf.length - TsiCpuPacket.SIZE_IPC_SIZE;
  169. // this.cvimData = new byte[TsiCpuPacket.SIZE_IPC_SIZE];
  170. // System.arraycopy(this.buf, 0, this.cvimData, 0, TsiCpuPacket.SIZE_IPC_SIZE);
  171. // if (nodeLength > 0) {
  172. // this.nodeData = new byte[nodeLength];
  173. // System.arraycopy(this.buf, TsiCpuPacket.SIZE_IPC_SIZE, this.nodeData, 0, nodeLength);
  174. // }
  175. // }
  176. protected byte getStx1() {
  177. if (this.buf != null) return this.buf[INDEX_STX1];
  178. return 0x00;
  179. }
  180. protected byte getStx2() {
  181. if (this.buf != null) return this.buf[INDEX_STX2];
  182. return 0x00;
  183. }
  184. public byte[] getCvimData() {
  185. return this.cvimData;
  186. }
  187. public byte[] getTestData() {
  188. return this.buf;
  189. }
  190. public byte[] getNodeData() {
  191. return this.nodeData;
  192. }
  193. /*
  194. * Make cvim-raw packet
  195. */
  196. protected void makeCvimPacket() {
  197. final int packetLength = (this.buf == null) ? 0 : this.buf.length;
  198. this.cvimData = new byte[SIZE_IPC_SIZE + packetLength];
  199. // cvim-raw header
  200. System.arraycopy(this.timespec.bytes(), 0, this.cvimData, POS_IPC_TIMESPEC, SIZE_TIMESPEC);
  201. ByteUtils.setUnsignedInt(this.cvimData, POS_IPC_IPADDR, getRemoteIp());
  202. ByteUtils.setUnsignedShort(this.cvimData, POS_IPC_PORT, getRemotePort());
  203. this.cvimData[POS_IPC_CONNECT] = (opCode == (byte)eOpCode.TSI_CPU_SIGNAL_NOTIFY.getValue()) ? CONNECT : DISCONNECT;
  204. ByteUtils.setUnsignedInt(this.cvimData, POS_IPC_NODEID, this.nodeId);
  205. // cvim-raw body
  206. if (packetLength > 0) {
  207. System.arraycopy(this.buf, 0, this.cvimData, POS_IPC_PACKET, packetLength);
  208. }
  209. //log.error("CVIM: {}", HexString.fromBytes(this.cvimData));
  210. }
  211. /*
  212. * Make cvim-raw packet
  213. */
  214. protected void makeAddNodeCvimPaket(TsiCpuPacket cpuPacket, byte[] packet) {
  215. final int packetLength = (packet == null) ? 0 : packet.length;
  216. final int headSize = SIZE_IPC_SIZE + 6;
  217. cpuPacket.cvimData = new byte[headSize + packetLength + 2]; // cpu packet 6 byte(nodeid 제외), crc 2 byte
  218. // cvim-raw header
  219. System.arraycopy(timespec.bytes(), 0, cpuPacket.cvimData, POS_IPC_TIMESPEC, SIZE_TIMESPEC);
  220. ByteUtils.setUnsignedInt(cpuPacket.cvimData, POS_IPC_IPADDR, getRemoteIp());
  221. ByteUtils.setUnsignedShort(cpuPacket.cvimData, POS_IPC_PORT, getRemotePort());
  222. cpuPacket.cvimData[POS_IPC_CONNECT] = (opCode == (byte)eOpCode.TSI_CPU_SIGNAL_NOTIFY.getValue()) ? CONNECT : DISCONNECT;
  223. ByteUtils.setUnsignedInt(cpuPacket.cvimData, POS_IPC_NODEID, cpuPacket.nodeId);
  224. cpuPacket.cvimData[SIZE_IPC_SIZE+INDEX_STX1] = getStx1();
  225. cpuPacket.cvimData[SIZE_IPC_SIZE+INDEX_STX2] = getStx2();
  226. ByteUtils.setUnsignedShort(cpuPacket.cvimData, SIZE_IPC_SIZE+INDEX_LENGTH, packetLength+2);
  227. cpuPacket.cvimData[SIZE_IPC_SIZE+INDEX_OPCODE] = (byte)getOpCode();
  228. cpuPacket.cvimData[SIZE_IPC_SIZE+INDEX_VERSION] = getDataVer();
  229. // cvim-raw body
  230. if (packetLength > 0) {
  231. System.arraycopy(packet, 0, cpuPacket.cvimData, headSize, packetLength);
  232. }
  233. // 체크섬 계산하지 않음
  234. }
  235. protected int checkPacket() {
  236. // 0 단계. STX1, STX2 체크
  237. if (this.buf[INDEX_STX1] != STX1 || this.buf[INDEX_STX2] != STX2) {
  238. // log.info("Node: {}, STX Error: {}, {}", nodeId, this.buf[INDEX_STX1], this.buf[INDEX_STX2]);
  239. // log.error("{}", HexString.fromBytes(this.buf));
  240. return -1;
  241. }
  242. // 1 단계. 패킷 길이 체크
  243. // TODO: 20240722, Protocol R28 ADDED
  244. // int reqLength = SIZE_PACKET_DATA + (SIZE_STATUS_DATA * this.count);
  245. // if (this.buf[INDEX_VERSION] == PROTOCOL_R28) {
  246. // reqLength = SIZE_PACKET_DATA + (SIZE_STATUS_DATA * this.count) + 1; // ADD CPU byte packet
  247. // }
  248. // if (this.length != reqLength ) {
  249. // log.info("Node: {}, Length Error: {}, Version: {}, status count: {}, {}", nodeId, this.length, this.buf[INDEX_VERSION],
  250. // this.count, reqLength);
  251. // log.error("{}", HexString.fromBytes(this.buf));
  252. // return false;
  253. // }
  254. int reqLength = SIZE_PACKET_DATA + (SIZE_STATUS_DATA * this.count);
  255. if (this.length < reqLength ) {
  256. // log.info("Node: {}, Length Error: {}, Version: {}, status count: {}, {}", nodeId, this.length, this.buf[INDEX_VERSION],
  257. // this.count, reqLength);
  258. // log.error("{}", HexString.fromBytes(this.buf));
  259. return -2;
  260. }
  261. // 2단계. 체크섬
  262. this.checkSum = ByteUtils.getUnsignedShort(this.buf, this.buf.length-2);
  263. this.calcCheckSum = CRC16Utils.CRC16_ccitt_cvim(this.buf, INDEX_LENGTH, this.length-2); // 시작인덱스가 있으므로 전체길이로 계산
  264. if (this.checkSum != this.calcCheckSum) {
  265. // log.error("Node: {}, Check Sum Error: Version: {}, recv: {}, calc: {}", nodeId, this.buf[INDEX_VERSION], this.checkSum, this.calcCheckSum);
  266. // log.error("{}", HexString.fromBytes(this.buf));
  267. return -3;
  268. }
  269. return 0;
  270. }
  271. public int parsing(TsiNodeVo obj, boolean isCheckPacket) {
  272. if (this.buf == null || this.buf.length < INDEX_STATUS_HDR+3) {
  273. return -4;
  274. }
  275. this.opCode = this.buf[INDEX_OPCODE];
  276. this.dataVer = this.buf[INDEX_VERSION];
  277. this.length = ByteUtils.getUnsignedShort(this.buf, INDEX_LENGTH);
  278. this.count = (int)(this.buf[INDEX_STATUS_HDR+3] & 0x7F);
  279. obj.setSigCount(this.count); // 신호현시 갯수 저장
  280. // CVIM 데이터 및 TEST 데이터가 생성됨
  281. makeCvimPacket();
  282. int result = checkPacket();
  283. if (0 != result) {
  284. if (isCheckPacket) {
  285. // 20250425: CRC 체크여부에 따라 바로 리턴(기본값은 체크여부가 true 임)
  286. return result;
  287. }
  288. }
  289. TsiNodeAddVo tsiNodeAddVo = this.nodeAddManager.get(this.nodeId);
  290. if (tsiNodeAddVo == null) {
  291. // 연등지 정보가 없는 경우
  292. // R25 인 경우 마지막 CPU 제조사 코드를 복사하지 않도록 수정
  293. int addLength = this.buf.length-SIZE_NODE_DUMMY;
  294. final int r25Length = SIZE_NODE_HEAD + (this.count * SIZE_STATUS_DATA);
  295. if (addLength > r25Length) {
  296. addLength = r25Length;
  297. }
  298. this.nodeData = new byte[addLength];
  299. System.arraycopy(this.buf, INDEX_NODE_ID, this.nodeData, 0, addLength);
  300. return 0;
  301. }
  302. // 연등지 교차로 데이터 파싱
  303. byte[] head = new byte[SIZE_NODE_HEAD];
  304. List<byte[]> nodeStatus = new ArrayList<>();
  305. Map<Long, List<byte[]>> addStatus = new HashMap<>();
  306. System.arraycopy(this.buf, INDEX_NODE_ID, head, 0, SIZE_NODE_HEAD);
  307. int loop = 0;
  308. for (int ii = INDEX_STATUS_DATA; loop < this.count; ii += SIZE_STATUS_DATA, loop++) {
  309. byte[] status = new byte[SIZE_STATUS_DATA];
  310. System.arraycopy(this.buf, ii, status, 0, SIZE_STATUS_DATA);
  311. int dirAdd = status[INDEX_STATUS_DIR_ADD] & 0x0F;
  312. if (dirAdd == 0) {
  313. // 원천 노드 정보
  314. nodeStatus.add(status);
  315. }
  316. else {
  317. // 추가 노드 정보
  318. int originDirCode = status[INDEX_STATUS_DIRECTION]; // 원천방향코드
  319. int direction = originDirCode/10; // 인천공항 노드 확인용
  320. int addNodeIdx = originDirCode%10; // 인천공항 노드 확인용
  321. if (addNodeIdx > 4) {
  322. // 인천공항 연등지 인 경우 2개 이상이기 때문에 연등지1 = 15, 연등지2=16, ....
  323. // 즉, 일의 자리 5,6,7,8,9로 설정하고 1,2,3,4,5로 된다. 십의자리는 *10을 해서 원천방향코드가 된다.
  324. dirAdd = addNodeIdx - 4; // 인천공항이 아닌경우는 위에서 구한 dirAdd 을 그대로 사용한다(1,2 만 가능)
  325. originDirCode = direction * 10; // 15,16,17,18,19=10, 25,26,27,28,29=20, ....
  326. }
  327. status[INDEX_STATUS_DIR_ADD] = (byte) (status[INDEX_STATUS_DIR_ADD] & 0xF0); // 방향추가정보를 0 으로 초기화
  328. TsiNodeAddDetailVo detailVo = tsiNodeAddVo.getAddNodeMap().get(originDirCode * 1000 + dirAdd);
  329. if (detailVo != null && detailVo.getAddDirCode() != null) {
  330. for (int dirIdx = 0; dirIdx < detailVo.getAddDirCode().length; dirIdx++) {
  331. // if (detailVo.getAddDirCode()[dirIdx] == (byte)0x00) {
  332. // continue;
  333. // }
  334. // 연등지 방향코드를 배열에 가지고 있기 때문에
  335. List<byte[]> list = addStatus.get(detailVo.getNodeId());
  336. if (list == null) {
  337. list = new ArrayList<>();
  338. addStatus.put(detailVo.getNodeId(), list);
  339. }
  340. if (dirIdx == 0) {
  341. status[INDEX_STATUS_DIRECTION] = detailVo.getAddDirCode()[dirIdx];
  342. list.add(status);
  343. }
  344. else {
  345. byte[] status2 = new byte[SIZE_STATUS_DATA];
  346. System.arraycopy(status, 0, status2, 0, SIZE_STATUS_DATA);
  347. status2[INDEX_STATUS_DIRECTION] = detailVo.getAddDirCode()[dirIdx];
  348. list.add(status2);
  349. }
  350. }
  351. }
  352. }
  353. }
  354. byte splitFlag = 0x01;
  355. int statusCount = nodeStatus.size();
  356. this.nodeData = new byte[SIZE_NODE_HEAD + (statusCount*SIZE_STATUS_DATA)];
  357. System.arraycopy(head, 0, this.nodeData, 0, SIZE_NODE_HEAD);
  358. this.nodeData[POS_NODE_HEAD_COUNT] = (byte) (statusCount | (splitFlag << 7));
  359. for (int ii = 0; ii < statusCount; ii++) {
  360. System.arraycopy(nodeStatus.get(ii), 0, this.nodeData, SIZE_NODE_HEAD+(ii*SIZE_STATUS_DATA), SIZE_STATUS_DATA);
  361. }
  362. makeAddNodeCvimPaket(this, this.nodeData);
  363. if (addStatus.isEmpty()) {
  364. // 연등지 정보가 설정된게 없을까???
  365. return 0;
  366. }
  367. // 연등지 노드 카프카 패킷 생성
  368. this.addNodes = new ArrayList<>();
  369. for (Map.Entry<Long, List<byte[]>> entry: addStatus.entrySet()) {
  370. statusCount = entry.getValue().size();
  371. TsiCpuAddPacket addPacket = new TsiCpuAddPacket(entry.getKey(), this.timespec, this.remoteIp, this.remotePort);
  372. addPacket.setObj(this.nodeAddManager.get(entry.getKey()));
  373. addPacket.setNodeData(new byte[SIZE_NODE_HEAD + (statusCount*SIZE_STATUS_DATA)]);
  374. System.arraycopy(head, 0, addPacket.getNodeData(), 0, SIZE_NODE_HEAD);
  375. ByteUtils.setUnsignedInt(addPacket.getNodeData(), POS_NODE_HEAD_NODEID, entry.getKey());
  376. addPacket.getNodeData()[POS_NODE_HEAD_COUNT] = (byte) (statusCount | (splitFlag << 7));
  377. for (int ii = 0; ii < statusCount; ii++) {
  378. System.arraycopy(entry.getValue().get(ii), 0, addPacket.getNodeData(), SIZE_NODE_HEAD+(ii*SIZE_STATUS_DATA), SIZE_STATUS_DATA);
  379. }
  380. makeAddNodeCvimPaket(addPacket, addPacket.getNodeData());
  381. addNodes.add(addPacket);
  382. }
  383. /*
  384. log.error(" RAW: {}, {}", this.nodeId, HexString.fromBytes(getBuf()));
  385. log.error("NODE: {}, {}", this.nodeId, HexString.fromBytes(getNodeData()));
  386. log.error("CVIM: {}, {}", this.nodeId, HexString.fromBytes(getCvimData()));
  387. for (int ii = 0; ii < addNodes.size(); ii++) {
  388. log.error("NODE: {}, {}", addNodes.get(ii).nodeId, HexString.fromBytes(addNodes.get(ii).getNodeData()));
  389. log.error("CVIM: {}, {}", addNodes.get(ii).nodeId, HexString.fromBytes(addNodes.get(ii).getCvimData()));
  390. }*/
  391. return 0;
  392. }
  393. public TcsNodeStatus getNodeStatus() {
  394. int nodeLength = this.buf.length - TsiCpuPacket.SIZE_IPC_SIZE;
  395. byte[] msec = new byte[8];
  396. byte[] nsec = new byte[8];
  397. System.arraycopy(this.cvimData, 0, msec, 0, 8);
  398. System.arraycopy(this.cvimData, 8, nsec, 0, 8);
  399. ByteBuffer byteMSec = ByteBuffer.wrap(msec);
  400. ByteBuffer byteNSec = ByteBuffer.wrap(nsec);
  401. byteMSec.order(ByteOrder.LITTLE_ENDIAN);
  402. byteNSec.order(ByteOrder.LITTLE_ENDIAN);
  403. long milliSeconds = byteMSec.getLong();
  404. long nanoSeconds = byteNSec.getLong();
  405. this.remoteIp = ByteUtils.getUnsignedInt(this.cvimData, TsiCpuPacket.POS_IPC_IPADDR);
  406. this.remotePort = ByteUtils.getUnsignedShort(this.cvimData, TsiCpuPacket.POS_IPC_PORT);
  407. /////
  408. getTimespec().tv_nsec(nanoSeconds); // recv tcp,
  409. Date date = new Date(milliSeconds * 1000L);
  410. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  411. sdf.setTimeZone(java.util.TimeZone.getTimeZone("GMT+9"));
  412. String collectTime = sdf.format(date);
  413. if (this.cvimData[TsiCpuPacket.POS_IPC_CONNECT] == TsiCpuPacket.DISCONNECT) {
  414. return TcsNodeStatus.builder()
  415. ._id(this.nodeId)
  416. .nodeId(this.nodeId)
  417. .isConnect(false)
  418. .collectTime(collectTime)
  419. .build();
  420. }
  421. if (nodeLength < TsiCpuPacket.SIZE_HEAD + TsiCpuPacket.SIZE_STATUS_HDR) {
  422. // packet error
  423. return null;
  424. }
  425. String tscDateTime;
  426. int cycleElapsedTime;
  427. TsiCvimControl tscControlInfo = new TsiCvimControl();
  428. TsiCvimAbnormal tscAbnormalInfo = new TsiCvimAbnormal();
  429. int signalStatusInfoCount;
  430. int divFlag;
  431. long localTime;
  432. List<TsiCvimStatus> signalStatusInfos = new ArrayList<>();
  433. //this.opCode = this.nodeData[TsiCpuPacket.INDEX_OPCODE];
  434. //byte dataVer = this.nodeData[TsiCpuPacket.INDEX_VERSION];
  435. //int length = ByteUtils.getUnsignedShort(this.nodeData, TsiCpuPacket.INDEX_LENGTH);
  436. //int count = (int)(this.nodeData[TsiCpuPacket.INDEX_STATUS_HDR+3] & 0x7F);
  437. byte control = this.cvimData[TsiCpuPacket.SIZE_IPC_SIZE+TsiCpuPacket.INDEX_STATUS_HDR+0];
  438. tscControlInfo.inManualControl = ((control ) & 0x01) == 0x01; //수동;
  439. tscControlInfo.inFlashingControl = ((control >> 1) & 0x01) == 0x01; //점멸;
  440. tscControlInfo.inLightsOutControl = ((control >> 2) & 0x01) == 0x01; //소등
  441. tscControlInfo.inActuationControl = ((control >> 3) & 0x01) == 0x01; //감응;
  442. tscControlInfo.inTransitionControl = ((control >> 4) & 0x01) == 0x01; //전이;
  443. byte abnormal = this.cvimData[TsiCpuPacket.SIZE_IPC_SIZE+TsiCpuPacket.INDEX_STATUS_HDR+1];
  444. tscAbnormalInfo.inSignalConflict = ((abnormal ) & 0x01) == 0x01; //모순상태;
  445. tscAbnormalInfo.inCenterComm = ((abnormal >> 1) & 0x01) == 0x01; //센터상태;
  446. tscAbnormalInfo.inScuComm = ((abnormal >> 2) & 0x01) == 0x01; //SCU 상태
  447. cycleElapsedTime = (int)(this.cvimData[TsiCpuPacket.SIZE_IPC_SIZE+TsiCpuPacket.INDEX_STATUS_HDR+2] & 0xFF);
  448. byte stts = this.cvimData[TsiCpuPacket.SIZE_IPC_SIZE+TsiCpuPacket.INDEX_STATUS_HDR+3];;
  449. signalStatusInfoCount = (int)(stts & 0x7F);
  450. //divFlag = (int)((stts >> 7) & 0x01);
  451. localTime = ByteUtils.getUnsignedInt(this.cvimData, TsiCpuPacket.SIZE_IPC_SIZE+TsiCpuPacket.INDEX_STATUS_HDR+4);
  452. int ii = TsiCpuPacket.SIZE_IPC_SIZE+TsiCpuPacket.INDEX_STATUS_DATA;
  453. for (int idx = 0; idx < signalStatusInfoCount; idx++) {
  454. TsiCvimStatus status = new TsiCvimStatus();
  455. //int dirAdd = (buffer.get(ii)) & 0x0F; //연등지
  456. int lightsType = (int)((this.cvimData[ii ] >> 4) & 0x0F);
  457. int lightsStatus = (int)((this.cvimData[ii+1] ) & 0x07);
  458. int reliability = (int)((this.cvimData[ii+1] >> 7) & 0x01);
  459. boolean readyPedestrian = ((this.cvimData[ii+1] >> 6) & 0x01) == 0x01;
  460. boolean unProtected = ((this.cvimData[ii+1] >> 3) & 0x01) == 0x01;
  461. int totalSeconds = (int)(this.cvimData[ii+2] & 0xFF);
  462. int remainSeconds = (int)(this.cvimData[ii+3] & 0xFF);
  463. int directionCode = (int)(this.cvimData[ii+4] & 0xFF);
  464. status.setLightsType(eLightsType.getByValue(lightsType)); // 신호등정보 [직진,좌,보행]
  465. status.setLightsStatus(eLightsStatus.getByValue(lightsStatus)); //신호등상태
  466. status.setTimeReliability(eTimeReliability.getByValue(reliability)); //시간정보신뢰성
  467. status.setReadyPedestrianSignal(readyPedestrian); //보행자
  468. status.setUnProtectedSignal(unProtected); //비보호 상태
  469. status.setTotalSeconds(totalSeconds); //표출시간
  470. status.setRemainingSeconds(remainSeconds); //잔여시간
  471. status.setDirectionCode(directionCode); //방향코드
  472. signalStatusInfos.add(status);
  473. ii += TsiCpuPacket.SIZE_STATUS_DATA;
  474. }
  475. date = new Date(localTime * 1000L);
  476. sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  477. sdf.setTimeZone(java.util.TimeZone.getTimeZone("GMT+9"));
  478. tscDateTime = sdf.format(date);
  479. //log.error("Packet Received: NodeId: {}, {}", this.nodeId, TimeUtils.elapsedTimeStr(System.nanoTime()-nanoSeconds));
  480. return TcsNodeStatus.builder()
  481. ._id(this.nodeId)
  482. .nodeId(this.nodeId)
  483. .isConnect(true)
  484. .collectTime(collectTime)
  485. .tscDateTime(tscDateTime)
  486. .cycleElapsedTime(cycleElapsedTime)
  487. .tscControlInfo(tscControlInfo)
  488. .tscAbnormalInfo(tscAbnormalInfo)
  489. .signalStatusInfoCount(signalStatusInfoCount)
  490. .signalStatusInfos(signalStatusInfos)
  491. .build();
  492. }
  493. }