综合平台编程器项目的远程存储
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 

296 рядки
7.8 KiB

  1. #include "software_logic_executor.h"
  2. #include <map>
  3. #include <type_traits>
  4. namespace {
  5. LogicScanResult success()
  6. {
  7. return {true, LogicScanError::None, {}, {}, {}, {}};
  8. }
  9. LogicScanResult failure(
  10. LogicScanError error,
  11. const std::string &message,
  12. const std::string &logic_id = {},
  13. const std::string &rung_id = {},
  14. const std::string &node_id = {})
  15. {
  16. return {false, error, message, logic_id, rung_id, node_id};
  17. }
  18. bool compareWord(
  19. std::int16_t actual,
  20. ComparisonOperator comparison,
  21. std::int16_t expected)
  22. {
  23. switch (comparison)
  24. {
  25. case ComparisonOperator::Equal:
  26. {
  27. return actual == expected;
  28. }
  29. case ComparisonOperator::NotEqual:
  30. {
  31. return actual != expected;
  32. }
  33. case ComparisonOperator::LessThan:
  34. {
  35. return actual < expected;
  36. }
  37. case ComparisonOperator::LessThanOrEqual:
  38. {
  39. return actual <= expected;
  40. }
  41. case ComparisonOperator::GreaterThan:
  42. {
  43. return actual > expected;
  44. }
  45. case ComparisonOperator::GreaterThanOrEqual:
  46. {
  47. return actual >= expected;
  48. }
  49. default:
  50. {
  51. return false;
  52. }
  53. }
  54. }
  55. } // namespace
  56. LogicScanResult SoftwareLogicExecutor::validate(
  57. const std::vector<ControlLogic> &logics) const
  58. {
  59. std::map<int, CoilMode> output_modes;
  60. for (const ControlLogic &logic : logics)
  61. {
  62. std::string validation_error;
  63. if (!logic.validateForRunning(&validation_error))
  64. {
  65. return failure(
  66. LogicScanError::InvalidLogic,
  67. validation_error,
  68. logic.id);
  69. }
  70. if (!logic.enabled)
  71. {
  72. continue;
  73. }
  74. for (const LadderRung &rung : logic.rungs)
  75. {
  76. if (!rung.output.has_value())
  77. {
  78. continue;
  79. }
  80. const auto *output = std::get_if<CoilNodeConfig>(&rung.output->config);
  81. if (output == nullptr)
  82. {
  83. return failure(
  84. LogicScanError::InvalidLogic,
  85. "ladder output is not a coil",
  86. logic.id,
  87. rung.id,
  88. rung.output->id);
  89. }
  90. const auto existing = output_modes.find(output->address.index());
  91. if (existing != output_modes.end() && existing->second != output->mode)
  92. {
  93. return failure(
  94. LogicScanError::ConflictingOutput,
  95. "mixed coil modes target " + output->address.toString(),
  96. logic.id,
  97. rung.id,
  98. rung.output->id);
  99. }
  100. output_modes[output->address.index()] = output->mode;
  101. }
  102. }
  103. return success();
  104. }
  105. LogicScanResult SoftwareLogicExecutor::executeScan(
  106. const std::vector<ControlLogic> &logics,
  107. RegisterRepository &repository) const
  108. {
  109. const LogicScanResult validation = validate(logics);
  110. if (!validation.succeeded)
  111. {
  112. return validation;
  113. }
  114. for (const ControlLogic &logic : logics)
  115. {
  116. if (!logic.enabled)
  117. {
  118. continue;
  119. }
  120. for (const LadderRung &rung : logic.rungs)
  121. {
  122. if (rung.stages.empty() && !rung.output.has_value())
  123. {
  124. continue;
  125. }
  126. bool rung_value = true;
  127. for (const LadderStage &stage : rung.stages)
  128. {
  129. bool stage_value = false;
  130. for (const LogicNode &node : stage.branches)
  131. {
  132. bool condition_value = false;
  133. LogicScanResult result = evaluateCondition(
  134. node, repository, &condition_value);
  135. if (!result.succeeded)
  136. {
  137. result.logicId = logic.id;
  138. result.rungId = rung.id;
  139. return result;
  140. }
  141. stage_value = stage_value || condition_value;
  142. }
  143. rung_value = rung_value && stage_value;
  144. }
  145. LogicScanResult result = writeOutput(
  146. *rung.output, rung_value, repository);
  147. if (!result.succeeded)
  148. {
  149. result.logicId = logic.id;
  150. result.rungId = rung.id;
  151. return result;
  152. }
  153. }
  154. }
  155. return success();
  156. }
  157. LogicScanResult SoftwareLogicExecutor::evaluateCondition(
  158. const LogicNode &node,
  159. RegisterRepository &repository,
  160. bool *value) const
  161. {
  162. if (value == nullptr)
  163. {
  164. return failure(
  165. LogicScanError::InvalidLogic,
  166. "condition result target is missing",
  167. {},
  168. {},
  169. node.id);
  170. }
  171. return std::visit(
  172. [&repository, value, &node](const auto &config) -> LogicScanResult
  173. {
  174. using Config = std::decay_t<decltype(config)>;
  175. if constexpr (std::is_same_v<Config, ContactNodeConfig>)
  176. {
  177. const BitReadResult read = repository.readBit(config.address);
  178. if (!read.succeeded)
  179. {
  180. return failure(
  181. LogicScanError::RegisterReadFailed,
  182. "failed to read " + config.address.toString(),
  183. {},
  184. {},
  185. node.id);
  186. }
  187. *value = config.mode == ContactMode::NormallyOpen
  188. ? read.value : !read.value;
  189. return success();
  190. }
  191. else if constexpr (std::is_same_v<Config, CompareNodeConfig>)
  192. {
  193. const WordReadResult read = repository.readWord(config.address);
  194. if (!read.succeeded)
  195. {
  196. return failure(
  197. LogicScanError::RegisterReadFailed,
  198. "failed to read " + config.address.toString(),
  199. {},
  200. {},
  201. node.id);
  202. }
  203. *value = compareWord(read.value, config.comparison, config.value);
  204. return success();
  205. }
  206. else
  207. {
  208. return failure(
  209. LogicScanError::InvalidLogic,
  210. "condition node contains an output coil",
  211. {},
  212. {},
  213. node.id);
  214. }
  215. },
  216. node.config);
  217. }
  218. LogicScanResult SoftwareLogicExecutor::writeOutput(
  219. const LogicNode &node,
  220. bool rung_value,
  221. RegisterRepository &repository) const
  222. {
  223. const auto *config = std::get_if<CoilNodeConfig>(&node.config);
  224. if (config == nullptr)
  225. {
  226. return failure(
  227. LogicScanError::InvalidLogic,
  228. "ladder output is not a coil",
  229. {},
  230. {},
  231. node.id);
  232. }
  233. bool should_write = true;
  234. bool output_value = rung_value;
  235. switch (config->mode)
  236. {
  237. case CoilMode::Normal:
  238. {
  239. break;
  240. }
  241. case CoilMode::Set:
  242. {
  243. should_write = rung_value;
  244. output_value = true;
  245. break;
  246. }
  247. case CoilMode::Reset:
  248. {
  249. should_write = rung_value;
  250. output_value = false;
  251. break;
  252. }
  253. default:
  254. {
  255. return failure(
  256. LogicScanError::InvalidLogic,
  257. "unsupported coil mode",
  258. {},
  259. {},
  260. node.id);
  261. }
  262. }
  263. if (!should_write)
  264. {
  265. return success();
  266. }
  267. const RegisterWriteResult write = repository.writeBit(
  268. config->address, output_value);
  269. if (!write.succeeded)
  270. {
  271. return failure(
  272. LogicScanError::RegisterWriteFailed,
  273. "failed to write " + config->address.toString(),
  274. {},
  275. {},
  276. node.id);
  277. }
  278. return success();
  279. }