plc.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. #include "plc.h"
  2. #include "flog.h"
  3. #include <arpa/inet.h>
  4. #include <errno.h>
  5. #include <fcntl.h>
  6. #include <poll.h>
  7. #include <pthread.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <sys/socket.h>
  12. #include <netinet/in.h>
  13. #include <netinet/tcp.h>
  14. #include <stdarg.h>
  15. #include <unistd.h>
  16. #define S7_PDU_REQ 0x01
  17. #define S7_PDU_ACKD 0x03
  18. #define COTP_CR 0xE0
  19. #define COTP_CC 0xD0
  20. #define COTP_DT 0xF0
  21. #define TS_BYTE 0x02
  22. #define SOCK_TIMEOUT_MS 3000
  23. typedef struct {
  24. uint8_t buf[1024];
  25. int len;
  26. } Frame;
  27. typedef struct {
  28. uint8_t data[PLC_MAX_BLOCK];
  29. int len;
  30. } PlcBlock;
  31. static PlcConfig g_cfg;
  32. static int g_inited = 0;
  33. static pthread_t g_thread;
  34. static int g_running = 0;
  35. static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
  36. static int g_sock = -1;
  37. static int g_connected = 0;
  38. static PlcBlock g_block;
  39. static uint16_t g_seq = 0;
  40. static char g_err[256];
  41. #define DBG(...) do { if (getenv("PLC_DEBUG")) fprintf(stderr, __VA_ARGS__); } while (0)
  42. static void set_err(const char *fmt, ...) {
  43. va_list ap;
  44. va_start(ap, fmt);
  45. vsnprintf(g_err, sizeof(g_err), fmt, ap);
  46. va_end(ap);
  47. }
  48. static int recv_exact(int fd, void *buf, size_t len) {
  49. size_t got = 0;
  50. while (got < len) {
  51. ssize_t r = recv(fd, (uint8_t *)buf + got, len - got, 0);
  52. if (r <= 0) {
  53. if (r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
  54. set_err("recv timeout");
  55. else
  56. set_err("recv failed: %s", strerror(errno));
  57. return -1;
  58. }
  59. got += (size_t)r;
  60. }
  61. return 0;
  62. }
  63. static int send_all(int fd, const void *buf, size_t len) {
  64. size_t sent = 0;
  65. while (sent < len) {
  66. ssize_t r = send(fd, (const uint8_t *)buf + sent, len - sent, MSG_NOSIGNAL);
  67. if (r <= 0) {
  68. if (r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
  69. set_err("send timeout");
  70. else
  71. set_err("send failed: %s", strerror(errno));
  72. return -1;
  73. }
  74. sent += (size_t)r;
  75. }
  76. return 0;
  77. }
  78. /* Wrap payload in TPKT + COTP-DT and send. */
  79. static int send_s7(int fd, const uint8_t *s7, int s7len) {
  80. uint8_t frame[4 + 3 + 1024];
  81. if (3 + s7len > (int)sizeof(frame) - 4) {
  82. set_err("s7 frame too large");
  83. return -1;
  84. }
  85. int total = 4 + 3 + s7len;
  86. frame[0] = 3;
  87. frame[1] = 0;
  88. frame[2] = (uint8_t)(total >> 8);
  89. frame[3] = (uint8_t)(total & 0xFF);
  90. frame[4] = 2; /* COTP DT header length */
  91. frame[5] = COTP_DT;
  92. frame[6] = 0x80; /* EOT + sequence 0 */
  93. memcpy(frame + 7, s7, s7len);
  94. return send_all(fd, frame, total);
  95. }
  96. /* Receive one TPKT frame, return COTP payload (sans 3-byte DT header). */
  97. static int recv_s7(int fd, uint8_t *out, int maxout, int *out_len) {
  98. uint8_t hdr[4];
  99. if (recv_exact(fd, hdr, 4) < 0) return -1;
  100. if (hdr[0] != 3) {
  101. set_err("bad TPKT version %d", hdr[0]);
  102. return -1;
  103. }
  104. int total = ((int)hdr[2] << 8) | hdr[3];
  105. if (total < 7 || total > 4 + 1024) {
  106. set_err("bad TPKT length %d", total);
  107. return -1;
  108. }
  109. uint8_t payload[1024];
  110. if (recv_exact(fd, payload, total - 4) < 0) return -1;
  111. if (payload[0] != 2 || payload[1] != COTP_DT) {
  112. set_err("expected COTP DT, got %02x %02x", payload[0], payload[1]);
  113. return -1;
  114. }
  115. int s7len = total - 4 - 3;
  116. if (s7len < 0 || s7len > maxout) {
  117. set_err("bad s7 length %d", s7len);
  118. return -1;
  119. }
  120. memcpy(out, payload + 3, s7len);
  121. *out_len = s7len;
  122. return 0;
  123. }
  124. static void put_u16(uint8_t *p, uint16_t v) {
  125. p[0] = (uint8_t)(v >> 8);
  126. p[1] = (uint8_t)(v & 0xFF);
  127. }
  128. static uint16_t get_u16(const uint8_t *p) {
  129. return (uint16_t)(((uint16_t)p[0] << 8) | p[1]);
  130. }
  131. /* ---- COTP Connection Request ---- */
  132. static int cotp_connect(int fd) {
  133. uint8_t pdu[22];
  134. int n = 0;
  135. pdu[n++] = 0x11; /* length (excl. this byte) */
  136. pdu[n++] = COTP_CR;
  137. pdu[n++] = 0x00; pdu[n++] = 0x00; /* dst ref */
  138. pdu[n++] = 0x00; pdu[n++] = 0x01; /* src ref */
  139. pdu[n++] = 0x00; /* class */
  140. pdu[n++] = 0xC1; pdu[n++] = 0x02; /* calling TSAP (local) */
  141. pdu[n++] = (uint8_t)((g_cfg.tsap_local >> 8) & 0xFF);
  142. pdu[n++] = (uint8_t)(g_cfg.tsap_local & 0xFF);
  143. pdu[n++] = 0xC2; pdu[n++] = 0x02; /* called TSAP (remote) */
  144. pdu[n++] = (uint8_t)((g_cfg.tsap_remote >> 8) & 0xFF);
  145. pdu[n++] = (uint8_t)(g_cfg.tsap_remote & 0xFF);
  146. pdu[n++] = 0xC0; pdu[n++] = 0x01; pdu[n++] = 0x0A; /* PDU size 1024 */
  147. uint8_t frame[4 + n];
  148. int total = 4 + n;
  149. frame[0] = 3; frame[1] = 0;
  150. frame[2] = (uint8_t)(total >> 8);
  151. frame[3] = (uint8_t)(total & 0xFF);
  152. memcpy(frame + 4, pdu, n);
  153. if (send_all(fd, frame, total) < 0) return -1;
  154. fprintf(stderr, "[plc] COTP CR (%d bytes): ", total);
  155. for (int i = 0; i < total; i++) fprintf(stderr, "%02x ", frame[i]);
  156. fprintf(stderr, "\n");
  157. uint8_t resp[64];
  158. int rlen;
  159. if (recv_exact(fd, resp, 4) < 0) {
  160. fprintf(stderr, "[plc] no CC response (reset after CR)\n");
  161. return -1;
  162. }
  163. if (resp[0] != 3) { set_err("CC bad TPKT version"); return -1; }
  164. int rtot = ((int)resp[2] << 8) | resp[3];
  165. if (rtot < 7) { set_err("CC too short"); return -1; }
  166. rlen = rtot - 4;
  167. if (recv_exact(fd, resp + 4, rlen) < 0) return -1;
  168. if (resp[4] != 0x0B /* LI=11 */ || resp[5] != COTP_CC) {
  169. /* Some stacks vary LI; only type must be CC. */
  170. if (resp[5] != COTP_CC) { set_err("expected COTP CC"); return -1; }
  171. }
  172. DBG("[plc] COTP connected (CC)\n");
  173. return 0;
  174. }
  175. /* ---- S7 Setup Communication (negotiate PDU length) ---- */
  176. static int s7_setup(int fd) {
  177. uint8_t req[18];
  178. req[0] = 0x32; req[1] = S7_PDU_REQ;
  179. req[2] = 0x00; req[3] = 0x00;
  180. put_u16(req + 4, ++g_seq);
  181. put_u16(req + 6, 0x0008); /* param len */
  182. put_u16(req + 8, 0x0000); /* data len */
  183. req[10] = 0xF0; req[11] = 0x00; /* setup communication */
  184. put_u16(req + 12, 0x0001); /* max AMQ caller */
  185. put_u16(req + 14, 0x0001); /* max AMQ callee */
  186. put_u16(req + 16, 0x01E0); /* pdu length 480 */
  187. if (send_s7(fd, req, sizeof(req)) < 0) return -1;
  188. uint8_t resp[1024];
  189. int rlen;
  190. if (recv_s7(fd, resp, sizeof(resp), &rlen) < 0) return -1;
  191. if (rlen < 12 || resp[0] != 0x32) {
  192. set_err("bad setup response");
  193. return -1;
  194. }
  195. /* Tolerate an error response; negotiation is non-critical. */
  196. DBG("[plc] setup done (%d bytes)\n", rlen);
  197. return 0;
  198. }
  199. /* ---- S7 Read Request ---- */
  200. static int s7_read(int fd, uint8_t *block, int len) {
  201. uint8_t req[10 + 14];
  202. req[0] = 0x32; req[1] = S7_PDU_REQ;
  203. req[2] = 0x00; req[3] = 0x00;
  204. put_u16(req + 4, ++g_seq);
  205. put_u16(req + 6, 0x000E); /* param len */
  206. put_u16(req + 8, 0x0000); /* data len */
  207. req[10] = 0x04; req[11] = 0x01; /* READ VAR, 1 item */
  208. req[12] = 0x12; /* spec */
  209. req[13] = 0x0A; /* addr spec length */
  210. req[14] = 0x10; /* syntax S7-ANY */
  211. req[15] = TS_BYTE; /* transport size: BYTE */
  212. put_u16(req + 16, (uint16_t)len);
  213. put_u16(req + 18, g_cfg.area == PLC_AREA_DB ? (uint16_t)g_cfg.db : 0);
  214. req[20] = (uint8_t)g_cfg.area;
  215. uint32_t addr = (uint32_t)g_cfg.read_start * 8u;
  216. req[21] = (uint8_t)(addr >> 16);
  217. req[22] = (uint8_t)(addr >> 8);
  218. req[23] = (uint8_t)(addr & 0xFF);
  219. if (send_s7(fd, req, sizeof(req)) < 0) return -1;
  220. uint8_t resp[1024];
  221. int rlen;
  222. if (recv_s7(fd, resp, sizeof(resp), &rlen) < 0) return -1;
  223. if (rlen < 14 || resp[0] != 0x32) {
  224. set_err("bad read response header");
  225. return -1;
  226. }
  227. int pdu_type = resp[1];
  228. if (pdu_type != S7_PDU_ACKD) {
  229. set_err("unexpected read pdu type %02x", pdu_type);
  230. return -1;
  231. }
  232. uint16_t seq = get_u16(resp + 4);
  233. uint16_t param_len = get_u16(resp + 6);
  234. uint16_t data_len = get_u16(resp + 8);
  235. uint8_t err_cls = resp[10], err_code = resp[11];
  236. if (err_cls != 0) {
  237. set_err("s7 error class %02x code %02x", err_cls, err_code);
  238. return -1;
  239. }
  240. if (seq != g_seq) {
  241. /* Real S7 PLCs echo the request sequence; some simulators reply
  242. * with their own counter. Log but do not fail. */
  243. DBG("[plc] seq warn: resp %u != sent %u\n", seq, g_seq);
  244. }
  245. int off = 12 + param_len;
  246. int avail = rlen - off;
  247. if (avail < 4) {
  248. set_err("read response truncated");
  249. return -1;
  250. }
  251. int dlen = data_len;
  252. if (dlen > avail) {
  253. DBG("[plc] data_len warn: field %u > avail %d, clamping\n",
  254. data_len, avail);
  255. dlen = avail;
  256. }
  257. const uint8_t *d = resp + off;
  258. /* item: rc(1) ts(1) bitlen(2) data */
  259. if (dlen < 4) { set_err("read data empty"); return -1; }
  260. uint8_t rc = d[0];
  261. uint8_t ts = d[1];
  262. uint16_t blen = get_u16(d + 2);
  263. if (rc != 0xFF) {
  264. set_err("read item rc=0x%02x", rc);
  265. return -1;
  266. }
  267. int nbytes = (ts == TS_BYTE) ? blen : blen / 8;
  268. if (nbytes > (int)data_len - 4) {
  269. set_err("read data length mismatch (%d > %d)", nbytes, data_len - 4);
  270. return -1;
  271. }
  272. if (nbytes > PLC_MAX_BLOCK) nbytes = PLC_MAX_BLOCK;
  273. memcpy(block, d + 4, nbytes);
  274. return nbytes;
  275. }
  276. static int tcp_connect_handshake(void) {
  277. int fd = socket(AF_INET, SOCK_STREAM, 0);
  278. if (fd < 0) { set_err("socket: %s", strerror(errno)); return -1; }
  279. struct sockaddr_in sa;
  280. memset(&sa, 0, sizeof(sa));
  281. sa.sin_family = AF_INET;
  282. sa.sin_port = htons((uint16_t)g_cfg.port);
  283. if (inet_pton(AF_INET, g_cfg.ip, &sa.sin_addr) != 1) {
  284. set_err("bad ip %s", g_cfg.ip);
  285. close(fd);
  286. return -1;
  287. }
  288. /* Non-blocking connect + poll, so a slow/unreachable PLC cannot stall
  289. * the reconnect loop. */
  290. int flags = fcntl(fd, F_GETFL, 0);
  291. fcntl(fd, F_SETFL, flags | O_NONBLOCK);
  292. int rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
  293. if (rc < 0 && errno != EINPROGRESS) {
  294. set_err("tcp connect: %s", strerror(errno));
  295. close(fd);
  296. return -1;
  297. }
  298. if (rc < 0) {
  299. struct pollfd pfd = { .fd = fd, .events = POLLOUT };
  300. int pr = poll(&pfd, 1, SOCK_TIMEOUT_MS);
  301. if (pr <= 0) {
  302. set_err(pr == 0 ? "tcp connect timeout" : "tcp connect: %s",
  303. strerror(errno));
  304. close(fd);
  305. return -1;
  306. }
  307. int soerr = 0;
  308. socklen_t sl = sizeof(soerr);
  309. if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &soerr, &sl) < 0 || soerr != 0) {
  310. set_err("tcp connect refused: %s", strerror(soerr));
  311. close(fd);
  312. return -1;
  313. }
  314. }
  315. fcntl(fd, F_SETFL, flags);
  316. struct timeval tv;
  317. tv.tv_sec = SOCK_TIMEOUT_MS / 1000;
  318. tv.tv_usec = (SOCK_TIMEOUT_MS % 1000) * 1000;
  319. setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
  320. setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
  321. int one = 1;
  322. setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
  323. if (cotp_connect(fd) < 0) { close(fd); return -1; }
  324. if (s7_setup(fd) < 0) {
  325. /* Setup is not fatal; keep the transport connection. */
  326. DBG("[plc] setup failed (%s), continuing\n", plc_last_error());
  327. }
  328. g_sock = fd;
  329. return 0;
  330. }
  331. static void *plc_thread(void *arg) {
  332. (void)arg;
  333. int was_err_logged = 0;
  334. while (g_running) {
  335. if (g_sock < 0) {
  336. if (tcp_connect_handshake() < 0) {
  337. if (!was_err_logged) {
  338. fprintf(stderr, "[plc] connect %s:%d failed: %s\n",
  339. g_cfg.ip, g_cfg.port, plc_last_error());
  340. flog("PLC 连接 %s:%d 失败: %s", g_cfg.ip, g_cfg.port, plc_last_error());
  341. was_err_logged = 1;
  342. }
  343. usleep((useconds_t)g_cfg.reconnect_ms * 1000);
  344. continue;
  345. }
  346. was_err_logged = 0;
  347. DBG("[plc] connected to %s:%d\n", g_cfg.ip, g_cfg.port);
  348. }
  349. uint8_t block[PLC_MAX_BLOCK];
  350. int n = s7_read(g_sock, block, g_cfg.read_len);
  351. if (n < 0) {
  352. fprintf(stderr, "[plc] read failed: %s\n", plc_last_error());
  353. flog_throttled("plc_read", 10000, "PLC 读取失败: %s", plc_last_error());
  354. close(g_sock);
  355. g_sock = -1;
  356. pthread_mutex_lock(&g_mutex);
  357. g_connected = 0;
  358. pthread_mutex_unlock(&g_mutex);
  359. usleep((useconds_t)g_cfg.reconnect_ms * 1000);
  360. continue;
  361. }
  362. pthread_mutex_lock(&g_mutex);
  363. memcpy(g_block.data, block, n);
  364. g_block.len = n;
  365. g_connected = 1;
  366. pthread_mutex_unlock(&g_mutex);
  367. DBG("[plc] read %d bytes ok\n", n);
  368. usleep((useconds_t)g_cfg.poll_ms * 1000);
  369. }
  370. return NULL;
  371. }
  372. int plc_init(const PlcConfig *cfg) {
  373. if (g_inited) return 0;
  374. if (!cfg || cfg->port <= 0 || cfg->read_len <= 0 ||
  375. cfg->read_len > PLC_MAX_BLOCK) {
  376. set_err("invalid plc config");
  377. return -1;
  378. }
  379. g_cfg = *cfg;
  380. g_cfg.ip[sizeof(g_cfg.ip) - 1] = '\0';
  381. if (g_cfg.poll_ms <= 0) g_cfg.poll_ms = 100;
  382. if (g_cfg.reconnect_ms <= 0) g_cfg.reconnect_ms = 3000;
  383. g_running = 1;
  384. g_sock = -1;
  385. g_connected = 0;
  386. if (pthread_create(&g_thread, NULL, plc_thread, NULL) != 0) {
  387. g_running = 0;
  388. set_err("pthread_create failed");
  389. return -1;
  390. }
  391. g_inited = 1;
  392. return 0;
  393. }
  394. void plc_shutdown(void) {
  395. if (!g_inited) return;
  396. g_running = 0;
  397. if (g_sock >= 0) {
  398. shutdown(g_sock, SHUT_RDWR);
  399. close(g_sock);
  400. g_sock = -1;
  401. }
  402. pthread_join(g_thread, NULL);
  403. g_inited = 0;
  404. }
  405. int plc_is_connected(void) {
  406. pthread_mutex_lock(&g_mutex);
  407. int c = g_connected;
  408. pthread_mutex_unlock(&g_mutex);
  409. return c;
  410. }
  411. int plc_get_bit(int byte_idx, int bit_idx) {
  412. int r = -1;
  413. pthread_mutex_lock(&g_mutex);
  414. if (g_connected && byte_idx >= 0 && byte_idx < g_block.len &&
  415. bit_idx >= 0 && bit_idx < 8) {
  416. r = (g_block.data[byte_idx] >> bit_idx) & 1;
  417. }
  418. pthread_mutex_unlock(&g_mutex);
  419. return r;
  420. }
  421. int plc_get_byte(int byte_idx, uint8_t *out) {
  422. int r = -1;
  423. pthread_mutex_lock(&g_mutex);
  424. if (g_connected && out && byte_idx >= 0 && byte_idx < g_block.len) {
  425. *out = g_block.data[byte_idx];
  426. r = 0;
  427. }
  428. pthread_mutex_unlock(&g_mutex);
  429. return r;
  430. }
  431. const char *plc_last_error(void) {
  432. pthread_mutex_lock(&g_mutex);
  433. if (g_err[0] == '\0') {
  434. pthread_mutex_unlock(&g_mutex);
  435. return "no error";
  436. }
  437. static char copy[256];
  438. strncpy(copy, g_err, sizeof(copy) - 1);
  439. copy[sizeof(copy) - 1] = '\0';
  440. pthread_mutex_unlock(&g_mutex);
  441. return copy;
  442. }