Parcourir la source

feat: 支持梯形图线圈置位复位配对

main
suyu il y a 1 mois
Parent
révision
0d2b4f0942
2 fichiers modifiés avec 62 ajouts et 6 suppressions
  1. +11
    -1
      app/src/services/software_logic_executor.cpp
  2. +51
    -5
      app/tests/offline_simulation_service_tests.cpp

+ 11
- 1
app/src/services/software_logic_executor.cpp Voir le fichier

@@ -58,6 +58,15 @@ bool compareWord(
}
}

bool coilModesAreCompatible(CoilMode existing, CoilMode current)
{
if (existing == current)
{
return true;
}
return existing != CoilMode::Normal && current != CoilMode::Normal;
}

} // namespace

void LogicTraceSnapshot::clear()
@@ -102,7 +111,8 @@ LogicScanResult SoftwareLogicExecutor::validate(
rung.output->id);
}
const auto existing = output_modes.find(output->address.index());
if (existing != output_modes.end() && existing->second != output->mode)
if (existing != output_modes.end()
&& !coilModesAreCompatible(existing->second, output->mode))
{
return failure(
LogicScanError::ConflictingOutput,


+ 51
- 5
app/tests/offline_simulation_service_tests.cpp Voir le fichier

@@ -231,17 +231,62 @@ void testSetResetAndDisabledLogic()
require(readBit(repository, 5), "disabled logic must not change outputs");
}

void testConflictingCoilsAreRejected()
void testSetResetPairOnSameAddress()
{
VirtualRegisterRepository repository;
SoftwareLogicExecutor executor;
const ControlLogic program = logic({
rung("low-level-rung",
{{comparison("low-level", 0,
ComparisonOperator::LessThanOrEqual, 30)}},
coil("pump-set", 20, CoilMode::Set)),
rung("high-level-rung",
{{comparison("high-level", 0,
ComparisonOperator::GreaterThanOrEqual, 80)}},
coil("pump-reset", 20, CoilMode::Reset))});

writeWord(repository, 0, 20);
require(executor.executeScan({program}, repository).succeeded,
"set/reset pair scan must succeed at the low limit");
require(readBit(repository, 20), "low level must latch the pump on");

writeWord(repository, 0, 50);
require(executor.executeScan({program}, repository).succeeded,
"set/reset pair scan must succeed inside the deadband");
require(readBit(repository, 20), "deadband must retain the latched on state");

writeWord(repository, 0, 90);
require(executor.executeScan({program}, repository).succeeded,
"set/reset pair scan must succeed at the high limit");
require(!readBit(repository, 20), "high level must reset the pump");

writeWord(repository, 0, 50);
require(executor.executeScan({program}, repository).succeeded,
"set/reset pair rescan must succeed inside the deadband");
require(!readBit(repository, 20), "deadband must retain the reset state");
}

void testConflictingCoilsAreRejected()
{
SoftwareLogicExecutor executor;
const ControlLogic normal_and_set = logic({
rung("normal-rung", {{contact("normal-input", 0)}}, coil("normal", 8)),
rung("set-rung", {{contact("set-input", 1)}}, coil("set", 8, CoilMode::Set))});
const LogicScanResult result = executor.validate({program});
require(!result.succeeded && result.error == LogicScanError::ConflictingOutput,
"mixed output modes for one address must be rejected");
require(result.rungId == "set-rung" && result.nodeId == "set",
const LogicScanResult set_result = executor.validate({normal_and_set});
require(!set_result.succeeded
&& set_result.error == LogicScanError::ConflictingOutput,
"normal and set coils for one address must be rejected");
require(set_result.rungId == "set-rung" && set_result.nodeId == "set",
"conflict error must identify the offending rung and node");

const ControlLogic reset_and_normal = logic({
rung("reset-rung", {{contact("reset-input", 0)}},
coil("reset", 9, CoilMode::Reset)),
rung("normal-rung", {{contact("normal-input", 1)}}, coil("normal", 9))});
const LogicScanResult reset_result = executor.validate({reset_and_normal});
require(!reset_result.succeeded
&& reset_result.error == LogicScanError::ConflictingOutput,
"reset and normal coils for one address must be rejected");
}

void testHmiSimulationClosedLoop()
@@ -348,6 +393,7 @@ int main(int argc, char *argv[])
testNestedSeriesParallelExpression();
testAllComparisons();
testSetResetAndDisabledLogic();
testSetResetPairOnSameAddress();
testConflictingCoilsAreRejected();
testHmiSimulationClosedLoop();
testSimulationLifecycleSnapshotAndFault();


Chargement…
Annuler
Enregistrer