Skip to content

[hardware test]: S7-1200 CPU 1212C V4.7.3, classic S7 — pass (13/13) #907

Closed

Description

@russwing

Source revision

140ccf0 (master, 2026-09-30)

Overall result

pass

PLC family, model, and order code

S7-1200, CPU 1212C; order code not recorded (the runner reports order_code_status: unavailable:AttributeError)

Firmware and security mode

V4.7.3, PUT/GET enabled, non-optimized fixture DBs (DB1 read-only, DB2 scratch)

Host environment

Python 3.11.2 (CPython), Linux x86_64; pytest-bdd 8.1.0, python-snap7 3.2.0; dependencies from uv sync --extra test

Run time (UTC)

2026-09-30T12:35:31Z (the scratch-write run; a read-only smoke run at 12:34:39Z also passed 5/5)

Sanitized report artifacts

JSON report
{
  "ended_at": "2026-09-30T12:35:34.443266+00:00",
  "environment": {
    "architecture": "x86_64",
    "implementation": "CPython",
    "os": "Linux",
    "os_release": "6.1.0-53-amd64",
    "pytest_bdd": "8.1.0",
    "python": "3.11.2",
    "python_snap7": "3.2.0"
  },
  "overall_result": "pass",
  "plc": {
    "cpu_state": "S7CpuStatusRun",
    "family": "S7-1200",
    "firmware": "V4.7.3",
    "model": "CPU 1212C",
    "order_code": "",
    "order_code_status": "unavailable:AttributeError",
    "protocol_path": "classic_s7",
    "rack": 0,
    "security_mode": "PUT/GET, non-optimized fixture DBs",
    "slot": 1,
    "tia_configuration": ""
  },
  "scenarios": [
    {
      "diagnostic": null,
      "duration_seconds": 0.25113688403507695,
      "scenario_id": "tests/real_plc/test_acceptance.py::test_establish_and_close_a_session",
      "status": "passed",
      "tags": [
        "classic_s7",
        "real_plc",
        "smoke"
      ]
    },
    {
      "diagnostic": null,
      "duration_seconds": 0.4527071010088548,
      "scenario_id": "tests/real_plc/test_acceptance.py::test_read_the_canonical_fixture",
      "status": "passed",
      "tags": [
        "classic_s7",
        "real_plc",
        "smoke"
      ]
    },
    {
      "diagnostic": null,
      "duration_seconds": 0.3205957099562511,
      "scenario_id": "tests/real_plc/test_acceptance.py::test_read_multiple_values_in_one_request",
      "status": "passed",
      "tags": [
        "classic_s7",
        "real_plc",
        "smoke"
      ]
    },
    {
      "diagnostic": null,
      "duration_seconds": 0.23170647805090994,
      "scenario_id": "tests/real_plc/test_acceptance.py::test_reconnect_after_a_clean_disconnect",
      "status": "passed",
      "tags": [
        "classic_s7",
        "real_plc",
        "smoke"
      ]
    },
    {
      "diagnostic": null,
      "duration_seconds": 0.20287593704415485,
      "scenario_id": "tests/real_plc/test_acceptance.py::test_repeated_operations_do_not_corrupt_the_session",
      "status": "passed",
      "tags": [
        "classic_s7",
        "real_plc",
        "smoke"
      ]
    },
    {
      "diagnostic": null,
      "duration_seconds": 0.1367337359697558,
      "scenario_id": "tests/real_plc/test_acceptance.py::test_roundtrip_a_value_and_restore_the_plc[INT]",
      "status": "passed",
      "tags": [
        "classic_s7",
        "real_plc",
        "write"
      ]
    },
    {
      "diagnostic": null,
      "duration_seconds": 0.1456899319891818,
      "scenario_id": "tests/real_plc/test_acceptance.py::test_roundtrip_a_value_and_restore_the_plc[REAL]",
      "status": "passed",
      "tags": [
        "classic_s7",
        "real_plc",
        "write"
      ]
    },
    {
      "diagnostic": null,
      "duration_seconds": 0.23675779800396413,
      "scenario_id": "tests/real_plc/test_acceptance.py::test_roundtrip_a_value_and_restore_the_plc[BYTE]",
      "status": "passed",
      "tags": [
        "classic_s7",
        "real_plc",
        "write"
      ]
    },
    {
      "diagnostic": null,
      "duration_seconds": 0.18378342100186273,
      "scenario_id": "tests/real_plc/test_acceptance.py::test_roundtrip_a_value_and_restore_the_plc[WORD]",
      "status": "passed",
      "tags": [
        "classic_s7",
        "real_plc",
        "write"
      ]
    },
    {
      "diagnostic": null,
      "duration_seconds": 0.13081139494897798,
      "scenario_id": "tests/real_plc/test_acceptance.py::test_roundtrip_a_value_and_restore_the_plc[DWORD]",
      "status": "passed",
      "tags": [
        "classic_s7",
        "real_plc",
        "write"
      ]
    },
    {
      "diagnostic": null,
      "duration_seconds": 0.13170612300746143,
      "scenario_id": "tests/real_plc/test_acceptance.py::test_roundtrip_a_value_and_restore_the_plc[DINT]",
      "status": "passed",
      "tags": [
        "classic_s7",
        "real_plc",
        "write"
      ]
    },
    {
      "diagnostic": null,
      "duration_seconds": 0.1353469750029035,
      "scenario_id": "tests/real_plc/test_acceptance.py::test_roundtrip_a_value_and_restore_the_plc[CHAR]",
      "status": "passed",
      "tags": [
        "classic_s7",
        "real_plc",
        "write"
      ]
    },
    {
      "diagnostic": null,
      "duration_seconds": 0.13723417703295127,
      "scenario_id": "tests/real_plc/test_acceptance.py::test_roundtrip_a_value_and_restore_the_plc[BOOL]",
      "status": "passed",
      "tags": [
        "classic_s7",
        "real_plc",
        "write"
      ]
    }
  ],
  "schema_version": "1.0",
  "source": {
    "commit": "140ccf08f7bb602b63d2fa074bdedaa5399d8431",
    "dirty": false,
    "kind": "git"
  },
  "started_at": "2026-09-30T12:35:31.589977+00:00",
  "tester": "@russwing"
}
JUnit XML (the hostname attribute redacted)
<?xml version="1.0" encoding="utf-8"?><testsuites name="pytest tests"><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="13" time="2.849" timestamp="2026-09-30T12:35:31.592957+00:00" hostname="redacted"><testcase classname="tests.real_plc.test_acceptance" name="test_establish_and_close_a_session" time="0.252" /><testcase classname="tests.real_plc.test_acceptance" name="test_read_the_canonical_fixture" time="0.453" /><testcase classname="tests.real_plc.test_acceptance" name="test_read_multiple_values_in_one_request" time="0.321" /><testcase classname="tests.real_plc.test_acceptance" name="test_reconnect_after_a_clean_disconnect" time="0.232" /><testcase classname="tests.real_plc.test_acceptance" name="test_repeated_operations_do_not_corrupt_the_session" time="0.203" /><testcase classname="tests.real_plc.test_acceptance" name="test_roundtrip_a_value_and_restore_the_plc[INT]" time="0.137" /><testcase classname="tests.real_plc.test_acceptance" name="test_roundtrip_a_value_and_restore_the_plc[REAL]" time="0.146" /><testcase classname="tests.real_plc.test_acceptance" name="test_roundtrip_a_value_and_restore_the_plc[BYTE]" time="0.237" /><testcase classname="tests.real_plc.test_acceptance" name="test_roundtrip_a_value_and_restore_the_plc[WORD]" time="0.184" /><testcase classname="tests.real_plc.test_acceptance" name="test_roundtrip_a_value_and_restore_the_plc[DWORD]" time="0.132" /><testcase classname="tests.real_plc.test_acceptance" name="test_roundtrip_a_value_and_restore_the_plc[DINT]" time="0.132" /><testcase classname="tests.real_plc.test_acceptance" name="test_roundtrip_a_value_and_restore_the_plc[CHAR]" time="0.136" /><testcase classname="tests.real_plc.test_acceptance" name="test_roundtrip_a_value_and_restore_the_plc[BOOL]" time="0.138" /></testsuite></testsuites>

Skips, failures, and notes

  • Smoke (read-only): 5/5. Scratch writes (--allow-write, DB2 only): 8/8 types (INT, REAL, BYTE, WORD, DWORD, DINT, CHAR, BOOL), each restored and read back. --allow-admin off.
  • Upload/download on DB2 (the manual check from Formalize real-PLC acceptance testing with Gherkin scenarios #840): the CPU refused upload(2) with S7ProtocolError: S7 protocol error (class=0x81, code=0x04): This service is not implemented on the module or a frame error was reported. download was not reached. DB2 was byte-identical afterwards (all 38 bytes compared).
  • Runner notes: the JUnit XML's hostname attribute records the test host (redacted above), and the order-code read fails with AttributeError.
  • Supersedes the earlier smoke-only run on eb6d9e3.

Safety confirmation

  • I reviewed the attachments and they contain no PLC address, credentials, private keys, plant/site identifiers, or proprietary PLC program.

Activity

  1. gijzelaerr commented on Sep 30, 2026

    @gijzelaerr
    Owner

    Thanks for the run! The order_code_status: unavailable:AttributeError was a bug in the runner: it read the wrong field of S7OrderCode. Fixed in #908, so the next run will record the order code.

  2. added
    hardware-testStructured result from a real PLC acceptance run
    test-result: passAll selected applicable real-PLC scenarios passed
    on Oct 2, 2026
  3. gijzelaerr commented on Oct 3, 2026

    @gijzelaerr
    Owner

    The runner fixes from #908 are now on master: the order code is read from the right S7OrderCode field, and the JUnit XML no longer contains hostname or the PLC address. If you get the chance to rerun on this CPU, please open it as a new result issue and link this one. I'll keep this one open until then, per the result policy.

    I also added the upload(2) refusal (class 0x81, code 0x04) to the docs in #911, under PLC feature availability and in the error reference.

  4. gijzelaerr commented on Oct 13, 2026

    @gijzelaerr
    Owner

    Recorded in the new Tested and Verified Hardware section of the PLC support matrix (#947). Thanks for the runs!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    hardware-testStructured result from a real PLC acceptance runtest-result: passAll selected applicable real-PLC scenarios passed

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions