plc.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903
  1. /******************************************************************************
  2. 版权所有:
  3. 文件名称: plc.c
  4. 文件版本: 01.01
  5. 创建作者: sunxi
  6. 创建日期: 2015-11-02
  7. 功能说明: 可编程逻辑
  8. 其它说明:
  9. 1、只有实点可以合并成双点,且一个实点只能参加一个双点合并。双点合并在转发点表中
  10. 第一个点必须为合位,第二个点必须为分位。
  11. 修改记录:
  12. */
  13. /*------------------------------- 头文件 --------------------------------------
  14. */
  15. #include "head.h"
  16. /*------------------------------- 宏定义 --------------------------------------
  17. */
  18. // 逻辑点定义
  19. // 逻辑点由16位构成
  20. // 高3位: 类型 0保留,其它为真正类型
  21. // 次高5位: 开关号,0为公共,其它为开关序号
  22. // 低8位: 点号, 0为无效点号,取值返回为0.
  23. #define LP_TYPE 0XE000 // 逻辑点类型
  24. #define LP_SW 0X1F00 // 开关号
  25. #define LP_INDEX 0X00FF // 点号
  26. #define LP_TYPE_YX 0X2000 // 遥信点
  27. #define LP_TYPE_YK 0X4000 // 遥控点
  28. #define LP_TYPE_YC 0X6000 // 遥测点
  29. //#define LP_TYPE_EV 0X8000 // 事件点
  30. #define LP_TYPE_PLC 0XA000 // PLC 点
  31. #define LP_TYPE_PLC_CST 0XC000 // PLC 常量
  32. #define LP_TYPE_PLC_OPT 0XE000 // PLC 操作符
  33. #define LP_SW_PUB 0
  34. #define LP_SW_BEGIN 1
  35. #define LP_SW_END 31
  36. #define PLC_LOGIC_NOT (LP_TYPE_PLC_OPT | 0X0001)
  37. #define PLC_LOGIC_AND (LP_TYPE_PLC_OPT | 0X0002)
  38. #define PLC_LOGIC_OR (LP_TYPE_PLC_OPT | 0X0003)
  39. #define PLC_LOGIC_XOR (LP_TYPE_PLC_OPT | 0X0004)
  40. /*------------------------------ 类型结构 -------------------------------------
  41. */
  42. //可编程逻辑文件头
  43. struct plc_config
  44. {
  45. // 配置文件头
  46. struct cfg_file_head cfh;
  47. //工程文件名
  48. s8 prj_name[64];
  49. // 表达式
  50. u32 exp_addr; // 地址
  51. u32 exp_num; // 个数
  52. u32 exp_len; // 表达式长度
  53. // 保留
  54. u32 re_addr; //地址
  55. u32 re_num; //个数
  56. //对应描述文件
  57. u32 rsc_version; // 版本
  58. u32 rsc_signature; // 签名
  59. u32 rsc_length; // 长度
  60. u16 rsc_crc; // CRC
  61. u16 reserved0;
  62. };
  63. struct di_db
  64. {
  65. // 初始化值
  66. u32 timeout; // 双点滤波超时值
  67. u16 ev_code[2]; // 两个单点的遥信事件代码,由slot和no组合而成,从转发点表来
  68. // 双点采集用
  69. u32 us0; // 超时原点
  70. u8 bChange; // 标识双点变位动作
  71. u8 value0; // 双点结果,滤波前,由遥信处理程序直接生成
  72. u8 value1; // 双点结果,滤波后,且根据转发点表完成取反操作
  73. struct timespec ts; // 动作时刻
  74. };
  75. /*------------------------------ 全局变量 -------------------------------------
  76. */
  77. // 逻辑点数组
  78. struct lp g_lp[LP_NUM];
  79. // PLC处理数据
  80. u32 g_plc_num;
  81. struct plc g_plc[PLC_EXP_NUM+1];
  82. u32 g_plc_merge_index; // 合并点在PLC表中的位置
  83. // 双点遥信结构
  84. struct di_db g_di_db[MAX_DPI_NUMBER];
  85. /*------------------------------ 函数声明 -------------------------------------
  86. */
  87. void plc_db_soe(EVENT_STRUCT *ev);
  88. /*------------------------------ 外部函数 -------------------------------------
  89. 外部函数供其它实体文件引用,必须仔细检查传入参数的合法性.
  90. */
  91. int plc_yx2index(u8 sw, u8 type)
  92. {
  93. if (sw > SWITCH_NUM_MAX)
  94. {
  95. return -1;
  96. }
  97. if (type == 0)
  98. {
  99. return -1;
  100. }
  101. type--;
  102. if (sw == 0)
  103. {
  104. return type;
  105. }
  106. else
  107. {
  108. sw--;
  109. return PUB_DI_NUM_ALL + (SW_DI_NUM_ALL * sw) + type;
  110. }
  111. return -2;
  112. }
  113. SOE_DEF *plc_yx2soe(u8 sw, u8 type)
  114. {
  115. if (sw > SWITCH_NUM_MAX)
  116. {
  117. return NULL;
  118. }
  119. if (type == 0)
  120. {
  121. return NULL;
  122. }
  123. type--;
  124. if (sw == 0)
  125. {
  126. if (type >= PUB_DI_NUM && type < PUB_DI_NUM_ALL)
  127. {
  128. return (SOE_DEF *)&tAllSOE[type - PUB_DI_NUM + EV_SW_NUM];
  129. }
  130. }
  131. else
  132. {
  133. if (type >= SW_DI_NUM && type < SW_DI_NUM_ALL)
  134. {
  135. return (SOE_DEF *)&tAllSOE[type - SW_DI_NUM];
  136. }
  137. }
  138. return NULL;
  139. }
  140. s32 plc_get_input(u16 ld)
  141. {
  142. u16 type, sw, no;
  143. s32 r;
  144. type = (ld & LP_TYPE);
  145. sw = (ld & LP_SW) >> 8;
  146. no = (ld & LP_INDEX);
  147. r = 0;
  148. switch (type)
  149. {
  150. case LP_TYPE_YX:
  151. r = GetRmtYxVal(sw, no);
  152. r = (r == SW_DI_TYPE_ON);
  153. break;
  154. case LP_TYPE_YC:
  155. r = GetRmtMeaVal(sw, no, NULL);
  156. break;
  157. case LP_TYPE_PLC:
  158. r = g_plc[no].result;
  159. break;
  160. case LP_TYPE_PLC_CST:
  161. break;
  162. }
  163. return r;
  164. }
  165. int plc_init_iec_table(void)
  166. {
  167. u16 i, j, ep;
  168. u16 v;
  169. int index;
  170. SOE_DEF *soe;
  171. // 处理单点,如果一个逻辑点对应多个通讯点,将除第一个通讯点外的其它通讯点放入PLC逻辑中
  172. for (i = 0; i < g_table_head->di_num; i++)
  173. {
  174. index = plc_yx2index(g_di_table[i].owner, g_di_table[i].indexno);
  175. if (index < 0)
  176. {
  177. continue;
  178. }
  179. // 将第一个通讯点滤除
  180. if (g_lp[index].cp == g_di_table[i].cp)
  181. {
  182. continue;
  183. }
  184. // 没有通讯点,不需要
  185. if (g_di_table[i].cp == 0)
  186. {
  187. continue;
  188. }
  189. // 除第一个通讯点外的其它通讯点,放入PLC逻辑中
  190. // 检查空间
  191. if (g_plc_num >= PLC_EXP_NUM)
  192. {
  193. rt_printf("g_plc_num单点越限(g_plc_num=%d,i=%d).\r\n", g_plc_num, i);
  194. return -1;
  195. }
  196. // 通讯点
  197. g_plc[g_plc_num].cp = g_di_table[i].cp;
  198. // 标志,目前只有故障记录标志
  199. soe = plc_yx2soe(g_di_table[i].owner, g_di_table[i].indexno);
  200. if (soe)
  201. {
  202. if (soe->soe_def & SOE_TYPE_FAULT)
  203. {
  204. g_plc[g_plc_num].flag = (PLC_FLAG_FAULT | (g_di_table[i].owner << 16));
  205. }
  206. }
  207. // 插入操作数
  208. ep = 0;
  209. v = LP_TYPE_YX;
  210. v |= (g_di_table[i].owner << 8) | g_di_table[i].indexno;
  211. g_plc[g_plc_num].exp[ep++] = v;
  212. // 单点取反
  213. if (g_di_table[i].is_inverse)
  214. {
  215. g_plc[g_plc_num].exp[ep++] = PLC_LOGIC_NOT;
  216. }
  217. // 结束符
  218. g_plc[g_plc_num].exp[ep++] = 0;
  219. g_plc_num++;
  220. }
  221. // 处理合并点
  222. g_plc_merge_index = g_plc_num;
  223. for (i = 0; i < g_table_head->dimerge_num; i++)
  224. {
  225. if (g_plc_num >= PLC_EXP_NUM)
  226. {
  227. rt_printf("g_plc_num合并点越限(g_plc_num=%d,i=%d).\r\n", g_plc_num, i);
  228. return -2;
  229. }
  230. g_plc[g_plc_num].cp = g_di_merge_table[i].cp;
  231. ep = 0;
  232. for (j = 0; j < MIX_YX_NUMBER; j++)
  233. {
  234. u8 no = g_di_merge_table[i].no[j] & 0xff;
  235. u8 owner = (g_di_merge_table[i].no[j] >> 8) & 0xff;
  236. // 组合点号为空代表结束,退出。
  237. if (g_di_merge_table[i].no[j] == 0)
  238. {
  239. break;
  240. }
  241. // 标志,目前只有故障记录标志
  242. soe = plc_yx2soe(owner, no);
  243. if (soe)
  244. {
  245. if (soe->soe_def & SOE_TYPE_FAULT)
  246. {
  247. g_plc[g_plc_num].flag = (PLC_FLAG_FAULT | (owner << 16));
  248. }
  249. }
  250. // 插入操作数
  251. v = LP_TYPE_YX | (owner << 8) | no;
  252. g_plc[g_plc_num].exp[ep++] = v;
  253. // 单点取反
  254. if (g_di_merge_table[i].is_inv[j])
  255. {
  256. g_plc[g_plc_num].exp[ep++] = PLC_LOGIC_NOT;
  257. }
  258. // 插入或运算符
  259. if (j)
  260. {
  261. g_plc[g_plc_num].exp[ep++] = PLC_LOGIC_OR;
  262. }
  263. }
  264. // 合并点取反
  265. if (g_di_merge_table[i].is_inv_all)
  266. {
  267. g_plc[g_plc_num].exp[ep++] = PLC_LOGIC_NOT;
  268. }
  269. // 结束符
  270. g_plc[g_plc_num].exp[ep++] = 0;
  271. // 检查长度是否越界
  272. if (ep > PLC_EXP_LEN)
  273. {
  274. rt_printf("plc_iec_table: 表达式太长(i=%d,j=%d,ep=%d).\r\n", i, j, ep);
  275. return -3;
  276. }
  277. g_plc_num++;
  278. }
  279. // g_plc[2].do_owner = 0;
  280. // g_plc[2].do_index = 1;
  281. return 0;
  282. }
  283. int plc_init(void)
  284. {
  285. int i, j, k, index, ret;
  286. u8 sw, no;
  287. u32 timeout[2] = {0};
  288. u32 timeout_add;
  289. // 初值
  290. for (i = 0; i < LP_NUM; i++)
  291. {
  292. g_lp[i].cp = 0;
  293. g_lp[i].db_index = -1;
  294. }
  295. if (equ_config_null())
  296. {
  297. return -1;
  298. }
  299. // 单点
  300. // 保存第一个单点通讯点号
  301. for (i = 0; i < g_table_head->di_num; i++)
  302. {
  303. index = plc_yx2index(g_di_table[i].owner, g_di_table[i].indexno);
  304. if (index < 0)
  305. {
  306. // 跳过备用点
  307. continue;
  308. }
  309. if (g_lp[index].cp == 0)
  310. {
  311. g_lp[index].cp = g_di_table[i].cp;
  312. }
  313. }
  314. // 双点
  315. if (g_table_head->di_db_num > MAX_DPI_NUMBER)
  316. {
  317. return -2;
  318. }
  319. for (i = 0; i < g_table_head->di_db_num; i++)
  320. {
  321. for (k = 0; k < 2; k++)
  322. {
  323. sw = (u8)(g_di_db_table[i].no[k] >> 8);
  324. no = (u8)(g_di_db_table[i].no[k]);
  325. // 得到检查索引
  326. index = plc_yx2index(sw, no);
  327. if (index < 0)
  328. {
  329. return -3;
  330. }
  331. // 一个逻辑点只能对应一个双点
  332. if (g_lp[index].db_index >= 0)
  333. {
  334. return -4;
  335. }
  336. g_lp[index].db_index = i;
  337. // 得到滤波时间
  338. for (j = 0; j < g_equ_config->di_num; j++)
  339. {
  340. if ((g_equ_config_di[j].owner == sw) && (g_equ_config_di[j].type == no))
  341. {
  342. g_di_db[i].ev_code[k] = (g_equ_config_di[j].slot << 8) | g_equ_config_di[j].index;
  343. timeout[k] = g_equ_config_di[j].filter_time * USTIMER_MS;
  344. break;
  345. }
  346. }
  347. // 初始化双点值
  348. if (j != g_equ_config->di_num)
  349. {
  350. // 双点标准定义中,分位在bit0,合位在bit1.在转发点表中合位在前,分位在后
  351. // 所以移位必须取反一下。
  352. int sft = !k;
  353. if (dido_di_is_on(g_equ_config_di[j].slot, g_equ_config_di[j].index))
  354. {
  355. g_di_db[i].value0 |= 1 << sft;
  356. }
  357. else
  358. {
  359. g_di_db[i].value0 &= ~(1 << sft);
  360. }
  361. g_di_db[i].value1 = g_di_db[i].value0;
  362. if (g_di_db_table[i].is_inv[k])
  363. {
  364. g_di_db[i].value1 ^= 1 << sft;
  365. }
  366. }
  367. else
  368. {
  369. rt_printf("双点需要实际通道对应(i=%d)\r\n", i);
  370. return -5;
  371. }
  372. }
  373. // 双点确认延时(两点滤波时间差),转化为us,再增加20ms,以确保双点变位成功
  374. // 2017-09-05:在电科院测试,遥信10ms滤波,双点没有变位信号。针对此情况修改如下:
  375. // 1、附加延时由固定的20ms改为最小滤波时间的1/4,这样10ms的遥信滤波,将有2.5ms的附加延时。
  376. // 初步评估2.5ms的延时相对于5ms的中断间隔是个合适的值,
  377. // 2、如果附加延时大于20ms则限制为20ms,防止延时过长。
  378. if (timeout[0] >= timeout[1])
  379. {
  380. g_di_db[i].timeout = (timeout[0] - timeout[1]);
  381. timeout_add = timeout[1] / 4;
  382. }
  383. else
  384. {
  385. g_di_db[i].timeout = (timeout[1] - timeout[0]);
  386. timeout_add = timeout[0] / 4;
  387. }
  388. if (timeout_add > 20 * USTIMER_MS)
  389. {
  390. timeout_add = 20 * USTIMER_MS;
  391. }
  392. g_di_db[i].timeout += timeout_add;
  393. }
  394. // 解析PLC文件
  395. // 将转发点表中的多余单点和合并点转入PLC模块
  396. ret = plc_init_iec_table();
  397. if (ret < 0)
  398. {
  399. ret -= 10;
  400. }
  401. plc_scan(0);
  402. return ret;
  403. }
  404. int plc_exit(void)
  405. {
  406. return 0;
  407. }
  408. u8 plc_db_value(int index)
  409. {
  410. return g_di_db[index].value1;
  411. }
  412. void plc_db_init_value(u8 slot, u8 no, u8 v)
  413. {
  414. int index, db_index;
  415. u16 ev_code;
  416. ev_code = (slot << 8) | no;
  417. // 得到检查逻辑点索引
  418. index = plc_yx2index(g_di_st[slot][no].owner, g_di_st[slot][no].type);
  419. if (index < 0)
  420. {
  421. return;
  422. }
  423. // 检查双点索引
  424. db_index = g_lp[index].db_index;
  425. if (db_index < 0)
  426. {
  427. return;
  428. }
  429. #if 1
  430. // 取消正在进行的双点处理过程
  431. g_di_db[db_index].bChange = 0;
  432. #else
  433. // 双点第一次变位,记录时标
  434. if (g_di_db[db_index].bChange == 0)
  435. {
  436. g_di_db[db_index].ts = ev->ts;
  437. g_di_db[db_index].bChange = 1;
  438. }
  439. #endif
  440. // 每次变位都重新获取超时起点和双点的值
  441. g_di_db[db_index].us0 = ustimer_get_origin();
  442. if (ev_code == g_di_db[db_index].ev_code[0])
  443. {
  444. index = 1;
  445. }
  446. else if (ev_code == g_di_db[db_index].ev_code[1])
  447. {
  448. index = 0;
  449. }
  450. else
  451. {
  452. rt_printf("plc_db_soe:ev_code err!\r\n");
  453. return;
  454. }
  455. // 滤波前的值
  456. if (v)
  457. {
  458. g_di_db[db_index].value0 |= 1 << index;
  459. }
  460. else
  461. {
  462. g_di_db[db_index].value0 &= ~(1 << index);
  463. }
  464. // 滤波后的值
  465. // 双点标准定义中,分位在bit0,合位在bit1.在转发点表中合位在前,分位在后
  466. // 所以移位必须取反一下。
  467. if (g_di_db_table[db_index].is_inv[!index])
  468. {
  469. v = !v;
  470. }
  471. if (v)
  472. {
  473. g_di_db[db_index].value1 |= (1 << index);
  474. }
  475. else
  476. {
  477. g_di_db[db_index].value1 &= ~(1 << index);
  478. }
  479. #if 0
  480. rt_printf("db_init:db=%d,index=%d,is_inv=%d,v0=%d,v1=%d\r\n",
  481. db_index,index,
  482. g_di_db_table[db_index].is_inv[!index],
  483. g_di_db[db_index].value0,
  484. g_di_db[db_index].value1);
  485. #endif
  486. return;
  487. }
  488. void plc_db_soe(EVENT_STRUCT *ev)
  489. {
  490. int index, db_index;
  491. u8 slot, no;
  492. slot = (ev->ev_code >> 8);
  493. no = ev->ev_code & 0xff;
  494. // 得到检查逻辑点索引
  495. index = plc_yx2index(g_di_st[slot][no].owner, g_di_st[slot][no].type);
  496. if (index < 0)
  497. {
  498. return;
  499. }
  500. // 检查双点索引
  501. db_index = g_lp[index].db_index;
  502. if (db_index < 0)
  503. {
  504. return;
  505. }
  506. // 双点第一次变位,记录时标
  507. if (g_di_db[db_index].bChange == 0)
  508. {
  509. g_di_db[db_index].ts = ev->ts;
  510. g_di_db[db_index].bChange = 1;
  511. }
  512. // 每次变位都重新获取超时起点和双点的值
  513. g_di_db[db_index].us0 = ustimer_get_origin();
  514. if (ev->ev_code == g_di_db[db_index].ev_code[0])
  515. {
  516. index = 1;
  517. }
  518. else if (ev->ev_code == g_di_db[db_index].ev_code[1])
  519. {
  520. index = 0;
  521. }
  522. else
  523. {
  524. rt_printf("plc_db_soe:ev_code err!\r\n");
  525. return;
  526. }
  527. if (ev->ev_value)
  528. {
  529. g_di_db[db_index].value0 |= 1 << index;
  530. }
  531. else
  532. {
  533. g_di_db[db_index].value0 &= ~(1 << index);
  534. }
  535. return;
  536. }
  537. // 双点滤波处理,得到滤波后的值
  538. static void plc_db_scan(void)
  539. {
  540. int i, value;
  541. uint32_t flags;
  542. for (i = 0; i < g_table_head->di_db_num; i++)
  543. {
  544. if (g_di_db[i].bChange)
  545. {
  546. // 屏蔽中断的情况下检查超时,防止超时和value不对应。
  547. value = -1;
  548. rt_irq_save(flags);
  549. if (ustimer_get_duration(g_di_db[i].us0) >= g_di_db[i].timeout)
  550. {
  551. value = g_di_db[i].value0;
  552. }
  553. rt_irq_restore(flags);
  554. if (value != -1)
  555. {
  556. g_di_db[i].bChange = 0;
  557. if (g_di_db_table[i].is_inv[0])
  558. {
  559. value ^= 2;
  560. }
  561. if (g_di_db_table[i].is_inv[1])
  562. {
  563. value ^= 1;
  564. }
  565. // 如果和旧值相等,说明信号已返回,不应送SOE。
  566. if (value != g_di_db[i].value1)
  567. {
  568. soe_record_dpi(i, value, &g_di_db[i].ts);
  569. g_di_db[i].value1 = value;
  570. }
  571. }
  572. }
  573. }
  574. return;
  575. }
  576. int plc_scan(int is_rec)
  577. {
  578. static s32 r[64];
  579. u16 *p, i, rp;
  580. // 双点扫描
  581. plc_db_scan();
  582. // PLC扫描
  583. for (i = 0; i < g_plc_num; i++)
  584. {
  585. p = g_plc[i].exp;
  586. rp = 0;
  587. while (*p)
  588. {
  589. // 操作数,直接压栈
  590. if (((*p) & LP_TYPE) != LP_TYPE_PLC_OPT)
  591. {
  592. r[rp++] = plc_get_input(*p);
  593. }
  594. // 运算符,出栈计算,结果压栈
  595. else
  596. {
  597. switch (*p)
  598. {
  599. case PLC_LOGIC_NOT:
  600. r[rp - 1] = (~r[rp - 1]) & 0x01;
  601. break;
  602. case PLC_LOGIC_AND:
  603. r[rp - 2] = r[rp - 1] & r[rp - 2];
  604. rp--;
  605. break;
  606. case PLC_LOGIC_OR:
  607. r[rp - 2] = r[rp - 1] | r[rp - 2];
  608. rp--;
  609. break;
  610. case PLC_LOGIC_XOR:
  611. r[rp - 2] = r[rp - 1] ^ r[rp - 2];
  612. rp--;
  613. break;
  614. default:
  615. return -1;
  616. }
  617. }
  618. p++;
  619. }
  620. // 全部计算完毕,rp应该等1.
  621. if (rp != 1)
  622. {
  623. rt_err_set(ERR_CODE_PLC, 0);
  624. rt_printf("plc_scan:i=%d,rp=%d\r\n", i, rp);
  625. }
  626. if (g_plc[i].result != r[rp - 1])
  627. {
  628. g_plc[i].result = r[rp - 1];
  629. // 开出
  630. if (g_plc[i].do_index)
  631. {
  632. int type;
  633. if (g_plc[i].result)
  634. {
  635. type = SW_DO_TYPE_ON;
  636. soe_record_opt(EV_PLC_HZ, ((g_plc[i].do_owner << 8) | g_plc[i].do_index));
  637. }
  638. else
  639. {
  640. type = SW_DO_TYPE_OFF;
  641. soe_record_opt(EV_PLC_TZ, ((g_plc[i].do_owner << 8) | g_plc[i].do_index));
  642. }
  643. if (g_plc[i].do_owner == 0)
  644. {
  645. sw_do_pub(g_plc[i].do_index - 1, type);
  646. }
  647. else
  648. {
  649. sw_do(g_plc[i].do_owner - 1, g_plc[i].do_index - 1, type);
  650. }
  651. }
  652. // 需要送主站
  653. if (g_plc[i].cp && is_rec)
  654. {
  655. u8 value = g_plc[i].result ? 1 : 0;
  656. soe_record_plc(i, value, g_plc[i].flag);
  657. }
  658. }
  659. }
  660. return 0;
  661. }
  662. /*------------------------------ 内部函数 -------------------------------------
  663. 内部函数以下划线‘_’开头,不需要检查参数的合法性.
  664. */
  665. /*------------------------------ 测试函数 -------------------------------------
  666. 一个实体文件必须带一个本模块的测试函数来进行单元测试,如果的确不方便在本模块中
  667. 进行单元测试,必须在此注明实际的测试位置(例如在哪个实体文件中使用哪个测试函数).
  668. */
  669. int plc_lp_printf(void)
  670. {
  671. int i,begin[5];
  672. begin[0] = 0;
  673. begin[1] = PUB_DI_NUM;
  674. begin[2] = PUB_DI_NUM_ALL;
  675. begin[3] = PUB_DI_NUM_ALL + SW_DI_NUM*SWITCH_NUM_MAX;
  676. begin[4] = PUB_DI_NUM_ALL + SW_DI_NUM*SWITCH_NUM_MAX + EV_SW_NUM*SWITCH_NUM_MAX;
  677. rt_printf("\r\n");
  678. for (i=0; i<LP_NUM; i++)
  679. {
  680. if(g_lp[i].cp == 0 && g_lp[i].db_index == -1)
  681. {
  682. continue;
  683. }
  684. rt_printf("%04d:通讯点:0x%04x, 双点索引:%02d, ", i,g_lp[i].cp,g_lp[i].db_index);
  685. // 公共实点
  686. if (i>= begin[0] && i<begin[1])
  687. {
  688. rt_printf("资源点:%s\r\n", g_pub_di_name[i]);
  689. }
  690. // 公共虚点
  691. else if (i>= begin[1] && i<begin[2])
  692. {
  693. rt_printf("资源点:%s\r\n", tAllSOE[(i-begin[1])+EV_SW_NUM].strName);
  694. }
  695. // 开关实点搜集
  696. else if (i>= begin[2] && i<begin[3])
  697. {
  698. rt_printf("资源点:开关%02d%s\r\n",(i-begin[2])/SW_DI_NUM,g_sw_di_name[(i-begin[2])%SW_DI_NUM]);
  699. }
  700. // 开关虚点搜集
  701. else if (i>= begin[3] && i<begin[4])
  702. {
  703. rt_printf("资源点:开关%02d%s\r\n", (i-begin[3])/EV_SW_NUM,tAllSOE[(i-begin[3])%EV_SW_NUM].strName);
  704. }
  705. }
  706. return 0;
  707. }
  708. int plc_info_printf(void)
  709. {
  710. u16 *p,i;
  711. rt_printf("\r\n可编程逻辑表(num=%d):\r\n",g_plc_num);
  712. for(i=0;i<g_plc_num;i++)
  713. {
  714. p = g_plc[i].exp;
  715. rt_printf("PLC%02d:地址=0x%04x,结果=%d,开出归属=%d,开出索引=%d.\r\n",
  716. i,g_plc[i].cp,g_plc[i].result,g_plc[i].do_owner,g_plc[i].do_index);
  717. rt_printf("EXP%02d:",i);
  718. while(*p)
  719. {
  720. // 操作数
  721. if((*p&LP_TYPE) != LP_TYPE_PLC_OPT)
  722. {
  723. rt_printf("%04x,",*p);
  724. }
  725. // 运算符
  726. else
  727. {
  728. switch(*p)
  729. {
  730. case PLC_LOGIC_NOT:
  731. rt_printf("~,");
  732. break;
  733. case PLC_LOGIC_AND:
  734. rt_printf("&,");
  735. break;
  736. case PLC_LOGIC_OR:
  737. rt_printf("|,");
  738. break;
  739. case PLC_LOGIC_XOR:
  740. rt_printf("^,");
  741. break;
  742. default:
  743. return -1;
  744. }
  745. }
  746. p++;
  747. }
  748. rt_printf("\r\n\r\n");
  749. }
  750. // 双点信息
  751. rt_printf("\r\n双点信息(num=%d):\r\n",g_table_head->di_db_num);
  752. rt_printf("timeout\tevcode0\tevcode1\tbChange\tv0\tv1\r\n");
  753. for(i=0;i<g_table_head->di_db_num;i++)
  754. {
  755. struct di_db *db;
  756. db = & g_di_db[i];
  757. rt_printf("%d\t%04x\t%04x\t%d\t%d\t%d\r\n",db->timeout,db->ev_code[0],db->ev_code[1],db->bChange,db->value0,db->value1);
  758. }
  759. return 0;
  760. }
  761. /*------------------------------ 文件结束 -------------------------------------
  762. */