<ExportFile>
  <StructuredView Guid="{d9b2b2cc-ea99-4c3b-aa42-1e5c49e65b84}">
<Single xml:space="preserve" Type="{3daac5e4-660e-42e4-9cea-3711b98bfb63}" Method="IArchivable">
  <Null Name="Profile" />
  <List2 Name="EntryList">
    <Single Type="{6198ad31-4b98-445c-927f-3258a0e82fe3}" Method="IArchivable">
      <Single Name="IsRoot" Type="bool">True</Single>
      <Single Name="MetaObject" Type="{81297157-7ec9-45ce-845e-84cab2b88ade}" Method="IArchivable">
        <Single Name="Guid" Type="System.Guid">76689472-8b42-4026-9ac6-2510a39dcddb</Single>
        <Single Name="ParentGuid" Type="System.Guid">00000000-0000-0000-0000-000000000000</Single>
        <Single Name="Name" Type="string">KLF200</Single>
        <Dictionary Type="{2c41fa04-1834-41c1-816e-303c7aa2c05b}" Name="Properties">
          <Entry>
            <Key>
              <Single Type="System.Guid">829a18f2-c514-4f6e-9634-1df173429203</Single>
            </Key>
            <Value>
              <Single Type="{829a18f2-c514-4f6e-9634-1df173429203}" Method="IArchivable">
                <Dictionary Type="{fa2ee218-a39b-4b6d-b249-49dbddbd168a}" Name="ParentObjects">
                  <Entry>
                    <Key>
                      <Single Type="System.Guid">d9b2b2cc-ea99-4c3b-aa42-1e5c49e65b84</Single>
                    </Key>
                    <Value>
                      <Single Type="System.Guid">05d070a2-43cc-48d2-8104-cb7dcf9f2249</Single>
                    </Value>
                  </Entry>
                </Dictionary>
              </Single>
            </Value>
          </Entry>
        </Dictionary>
        <Single Name="TypeGuid" Type="System.Guid">738bea1e-99bb-4f04-90bb-a7a567e74e3a</Single>
        <Null Name="EmbeddedTypeGuids" />
        <Single Name="Timestamp" Type="long">639204336096690009</Single>
      </Single>
      <Single Name="Object" Type="{738bea1e-99bb-4f04-90bb-a7a567e74e3a}" Method="IArchivable">
        <Single Name="StructuredViewGuid" Type="System.Guid">d9b2b2cc-ea99-4c3b-aa42-1e5c49e65b84</Single>
      </Single>
      <Single Name="ParentSVNodeGuid" Type="System.Guid">05d070a2-43cc-48d2-8104-cb7dcf9f2249</Single>
      <Array Name="Path" Type="string">
        <Single Type="string">Device</Single>
        <Single Type="string">PLC Logic</Single>
        <Single Type="string">Application</Single>
      </Array>
      <Single Name="Index" Type="int">-1</Single>
    </Single>
    <Single Type="{6198ad31-4b98-445c-927f-3258a0e82fe3}" Method="IArchivable">
      <Single Name="IsRoot" Type="bool">False</Single>
      <Single Name="MetaObject" Type="{81297157-7ec9-45ce-845e-84cab2b88ade}" Method="IArchivable">
        <Single Name="Guid" Type="System.Guid">759397f7-34db-4348-9d25-7cf27ba2eb9a</Single>
        <Single Name="ParentGuid" Type="System.Guid">05d070a2-43cc-48d2-8104-cb7dcf9f2249</Single>
        <Single Name="Name" Type="string">gvlKLF200</Single>
        <Dictionary Type="{2c41fa04-1834-41c1-816e-303c7aa2c05b}" Name="Properties">
          <Entry>
            <Key>
              <Single Type="System.Guid">829a18f2-c514-4f6e-9634-1df173429203</Single>
            </Key>
            <Value>
              <Single Type="{829a18f2-c514-4f6e-9634-1df173429203}" Method="IArchivable">
                <Dictionary Type="{fa2ee218-a39b-4b6d-b249-49dbddbd168a}" Name="ParentObjects">
                  <Entry>
                    <Key>
                      <Single Type="System.Guid">d9b2b2cc-ea99-4c3b-aa42-1e5c49e65b84</Single>
                    </Key>
                    <Value>
                      <Single Type="System.Guid">76689472-8b42-4026-9ac6-2510a39dcddb</Single>
                    </Value>
                  </Entry>
                </Dictionary>
              </Single>
            </Value>
          </Entry>
        </Dictionary>
        <Single Name="TypeGuid" Type="System.Guid">ffbfa93a-b94d-45fc-a329-229860183b1d</Single>
        <Array Name="EmbeddedTypeGuids" Type="System.Guid">
          <Single Type="System.Guid">a9ed5b7e-75c5-4651-af16-d2c27e98cb94</Single>
        </Array>
        <Single Name="Timestamp" Type="long">639206620635903964</Single>
      </Single>
      <Single Name="Object" Type="{ffbfa93a-b94d-45fc-a329-229860183b1d}" Method="IArchivable">
        <Single Name="Interface" Type="{a9ed5b7e-75c5-4651-af16-d2c27e98cb94}" Method="IArchivable">
          <Single Name="TextDocument" Type="{f3878285-8e4f-490b-bb1b-9acbb7eb04db}" Method="IArchivable">
            <Single Name="TextBlobForSerialisation" Type="string">{attribute 'qualified_only'}
VAR_GLOBAL
END_VAR

VAR_GLOBAL CONSTANT
    KLF200_IP           : STRING(15) := '192.168.2.42';
    KLF200_PORT         : UINT       := 51200;
    KLF200_PASSWORD     : STRING(32) := 'BVcqF5gRPw';

    // Gateway command codes (Appendix 4, KLF200 API v3.18)
    CMD_PASSWORD_ENTER_REQ : WORD := 16#3000;
    CMD_PASSWORD_ENTER_CFM : WORD := 16#3001;

    SLIP_END     : BYTE := 16#C0;
    SLIP_ESC     : BYTE := 16#DB;
    SLIP_ESC_END : BYTE := 16#DC;
    SLIP_ESC_ESC : BYTE := 16#DD;

    MAX_FRAME_LEN : UDINT := 256; // command+data, well within the 253 byte spec limit
    MAX_SLIP_LEN  : UDINT := 530; // worst case: every byte escaped, + 2 END markers
END_VAR
</Single>
            <Single Name="LineInfoPersistence" Type="string">759397f7-34db-4348-9d25-7cf27ba2eb9a_Decl_LineIds</Single>
          </Single>
        </Single>
        <Null Name="NetVarProperties" />
        <Single Name="ParameterList" Type="bool">False</Single>
        <Single Name="AddAttributeSubsequent" Type="bool">False</Single>
      </Single>
      <Single Name="ParentSVNodeGuid" Type="System.Guid">76689472-8b42-4026-9ac6-2510a39dcddb</Single>
      <Array Name="Path" Type="string">
        <Single Type="string">Device</Single>
        <Single Type="string">PLC Logic</Single>
        <Single Type="string">Application</Single>
        <Single Type="string">KLF200</Single>
      </Array>
      <Single Name="Index" Type="int">-1</Single>
    </Single>
    <Single Type="{6198ad31-4b98-445c-927f-3258a0e82fe3}" Method="IArchivable">
      <Single Name="IsRoot" Type="bool">False</Single>
      <Single Name="MetaObject" Type="{81297157-7ec9-45ce-845e-84cab2b88ade}" Method="IArchivable">
        <Single Name="Guid" Type="System.Guid">c1ae5e64-a10b-4d3c-9826-c15e44272851</Single>
        <Single Name="ParentGuid" Type="System.Guid">05d070a2-43cc-48d2-8104-cb7dcf9f2249</Single>
        <Single Name="Name" Type="string">gvlKLF200Conn</Single>
        <Dictionary Type="{2c41fa04-1834-41c1-816e-303c7aa2c05b}" Name="Properties">
          <Entry>
            <Key>
              <Single Type="System.Guid">24568a24-c491-472c-a21f-ee5d33859fab</Single>
            </Key>
            <Value>
              <Single Type="{24568a24-c491-472c-a21f-ee5d33859fab}" Method="IArchivable">
                <Single Name="MemoryReserveForOnlineChange" Type="int">0</Single>
                <Single Name="ExcludeFromBuild" Type="bool">False</Single>
                <Single Name="External" Type="bool">False</Single>
                <Single Name="EnableSystemCall" Type="bool">False</Single>
                <Single Name="CompilerDefines" Type="string"></Single>
                <Single Name="LinkAlways" Type="bool">False</Single>
                <Array Name="Undefines" Type="string" />
              </Single>
            </Value>
          </Entry>
          <Entry>
            <Key>
              <Single Type="System.Guid">829a18f2-c514-4f6e-9634-1df173429203</Single>
            </Key>
            <Value>
              <Single Type="{829a18f2-c514-4f6e-9634-1df173429203}" Method="IArchivable">
                <Dictionary Type="{fa2ee218-a39b-4b6d-b249-49dbddbd168a}" Name="ParentObjects">
                  <Entry>
                    <Key>
                      <Single Type="System.Guid">d9b2b2cc-ea99-4c3b-aa42-1e5c49e65b84</Single>
                    </Key>
                    <Value>
                      <Single Type="System.Guid">76689472-8b42-4026-9ac6-2510a39dcddb</Single>
                    </Value>
                  </Entry>
                </Dictionary>
              </Single>
            </Value>
          </Entry>
        </Dictionary>
        <Single Name="TypeGuid" Type="System.Guid">ffbfa93a-b94d-45fc-a329-229860183b1d</Single>
        <Array Name="EmbeddedTypeGuids" Type="System.Guid">
          <Single Type="System.Guid">a9ed5b7e-75c5-4651-af16-d2c27e98cb94</Single>
        </Array>
        <Single Name="Timestamp" Type="long">639206620965903356</Single>
      </Single>
      <Single Name="Object" Type="{ffbfa93a-b94d-45fc-a329-229860183b1d}" Method="IArchivable">
        <Single Name="Interface" Type="{a9ed5b7e-75c5-4651-af16-d2c27e98cb94}" Method="IArchivable">
          <Single Name="TextDocument" Type="{f3878285-8e4f-490b-bb1b-9acbb7eb04db}" Method="IArchivable">
            <Single Name="TextBlobForSerialisation" Type="string">(* ------------------------------------------------------------------------
   gvlKLF200Conn - TLS/TCP connection objects, declared at GLOBAL scope
   deliberately, NOT inside FB_KLF200_Client.

   Reason: NBS.TLSContext / NBS.TCP_Client are FB-typed and use inline
   ":= (...)" start-value initialization. CODESYS has a documented issue
   where inline start values on FB-typed variables are NOT reliably applied
   when that variable is declared one level deep inside another function
   block - which silently left our TLSContext blank and caused TCP_Client
   to fall back to plain TCP with no TLS handshake at all (confirmed via
   packet capture: no ClientHello ever appears on the wire). Declaring them
   here at global scope avoids the nesting entirely.
------------------------------------------------------------------------ *)
VAR_GLOBAL
    ipAddress : NBS.IPv4Address := (ipAddress := gvlKLF200.KLF200_IP);

    // sCipherList is a STRUCT (NBS.CIPHER_LIST: psList/udiSize), NOT a plain
    // string - leaving it unset meant tlsContext carried a null pointer /
    // zero size for its cipher list rather than any real default, despite
    // docs claiming an implicit 'HIGH' default. Wiring it explicitly here,
    // same pattern as working NBS/MQTT client examples.
    _sCipherListStr : STRING := 'HIGH';
    _sCipherList : NBS.CIPHER_LIST := (psList := ADR(_sCipherListStr), udiSize := TO_UDINT(LEN(_sCipherListStr)));

    // Custom certificate verifier - overrides built-in expiry rejection.
    // See FB_KLF200_CertVerifier's header comment for why this is needed.
    certVerifier : FB_KLF200_CertVerifier;

    tlsContext : NBS.TLSContext := (
        sUseCaseName        := 'KLF200',
        ePurpose            := NBS.PURPOSE.CLIENT_SIDE,
        sTLSVersion         := '1.2',
        sCipherList         := _sCipherList,
        sHostname           := '', // KLF200 uses an IP + self-signed cert, no real hostname to validate
        udiVerificationMode := 1,  // per CODESYS help: 1 = "No peer verification" (the actual
                                   // documented value - 0 isn't documented at all). Testing
                                   // showed this alone doesn't bypass expiry though, hence
                                   // the custom verifier below as the real fix.
        itfCertVerifer      := certVerifier
    );

    // itfTLSContext MUST be wired here, at declaration time, not just as a
    // per-scan call parameter - confirmed by forge.codesys.com/forge/talk/
    // Engineering/thread/5c013b9bfe/ as the actual fix for "no ClientHello
    // ever appears" with this library family. Also per that thread, no
    // separate Upgrade() call is needed once this is set correctly - TLS
    // engages automatically as part of the connection becoming active.
    tcpClient : NBS.TCP_Client := (itfTLSContext := tlsContext);
END_VAR

</Single>
            <Single Name="LineInfoPersistence" Type="string">c1ae5e64-a10b-4d3c-9826-c15e44272851_Decl_LineIds</Single>
          </Single>
        </Single>
        <Null Name="NetVarProperties" />
        <Single Name="ParameterList" Type="bool">False</Single>
        <Single Name="AddAttributeSubsequent" Type="bool">False</Single>
      </Single>
      <Single Name="ParentSVNodeGuid" Type="System.Guid">76689472-8b42-4026-9ac6-2510a39dcddb</Single>
      <Array Name="Path" Type="string">
        <Single Type="string">Device</Single>
        <Single Type="string">PLC Logic</Single>
        <Single Type="string">Application</Single>
        <Single Type="string">KLF200</Single>
      </Array>
      <Single Name="Index" Type="int">-1</Single>
    </Single>
    <Single Type="{6198ad31-4b98-445c-927f-3258a0e82fe3}" Method="IArchivable">
      <Single Name="IsRoot" Type="bool">False</Single>
      <Single Name="MetaObject" Type="{81297157-7ec9-45ce-845e-84cab2b88ade}" Method="IArchivable">
        <Single Name="Guid" Type="System.Guid">6c08f547-ec6c-433e-9a64-8db562df68d0</Single>
        <Single Name="ParentGuid" Type="System.Guid">05d070a2-43cc-48d2-8104-cb7dcf9f2249</Single>
        <Single Name="Name" Type="string">FB_KLF200_CertVerifier</Single>
        <Dictionary Type="{2c41fa04-1834-41c1-816e-303c7aa2c05b}" Name="Properties">
          <Entry>
            <Key>
              <Single Type="System.Guid">829a18f2-c514-4f6e-9634-1df173429203</Single>
            </Key>
            <Value>
              <Single Type="{829a18f2-c514-4f6e-9634-1df173429203}" Method="IArchivable">
                <Dictionary Type="{fa2ee218-a39b-4b6d-b249-49dbddbd168a}" Name="ParentObjects">
                  <Entry>
                    <Key>
                      <Single Type="System.Guid">d9b2b2cc-ea99-4c3b-aa42-1e5c49e65b84</Single>
                    </Key>
                    <Value>
                      <Single Type="System.Guid">76689472-8b42-4026-9ac6-2510a39dcddb</Single>
                    </Value>
                  </Entry>
                </Dictionary>
              </Single>
            </Value>
          </Entry>
        </Dictionary>
        <Single Name="TypeGuid" Type="System.Guid">6f9dac99-8de1-4efc-8465-68ac443b7d08</Single>
        <Array Name="EmbeddedTypeGuids" Type="System.Guid">
          <Single Type="System.Guid">a9ed5b7e-75c5-4651-af16-d2c27e98cb94</Single>
          <Single Type="System.Guid">3b83b776-fb25-43b8-99f2-3c507c9143fc</Single>
        </Array>
        <Single Name="Timestamp" Type="long">639206619455856361</Single>
      </Single>
      <Single Name="Object" Type="{6f9dac99-8de1-4efc-8465-68ac443b7d08}" Method="IArchivable">
        <Single Name="SpecialFunc" Type="{0db3d7bb-cde0-4416-9a7b-ce49a0124323}">None</Single>
        <Single Name="Implementation" Type="{3b83b776-fb25-43b8-99f2-3c507c9143fc}" Method="IArchivable">
          <Single Name="TextDocument" Type="{f3878285-8e4f-490b-bb1b-9acbb7eb04db}" Method="IArchivable">
            <Single Name="TextBlobForSerialisation" Type="string">;

</Single>
            <Single Name="LineInfoPersistence" Type="string">6c08f547-ec6c-433e-9a64-8db562df68d0_Impl_LineIds</Single>
          </Single>
        </Single>
        <Single Name="Interface" Type="{a9ed5b7e-75c5-4651-af16-d2c27e98cb94}" Method="IArchivable">
          <Single Name="TextDocument" Type="{f3878285-8e4f-490b-bb1b-9acbb7eb04db}" Method="IArchivable">
            <Single Name="TextBlobForSerialisation" Type="string">(* ------------------------------------------------------------------------
   FB_KLF200_CertVerifier - custom ICertificateVerifier implementation.

   WHY THIS EXISTS: the KLF200's factory certificate expired 2026-07-12 and
   Velux provides no user-facing renewal. udiVerificationMode's documented
   values (1=No peer verification, 2=Active peer verification, +4/+8 =
   client-cert-related flags) turned out to have NO effect on this failure
   in testing (1 and other values produced an identical CONNECTION_ERROR),
   which suggests those flags govern SERVER-side behavior (requesting a
   client cert) rather than whether OUR client validates the KLF200's own
   certificate. itfCertVerifer is the actual override point for that.

   Confirmed via packet capture: the full TLS handshake completes correctly
   with the genuine KLF200 (its Certificate message was inspected and is
   the real Velux-issued cert) - the ONLY problem is date-based expiry
   validation. Since we're connecting directly to a physically-owned device
   on an isolated local network (not the open internet), and the identity
   of the peer is not in question here, this verifier accepts unconditionally
   rather than replicating full fingerprint-pinning logic (which would need
   an unconfirmed API for reading properties out of hCert).

   If you want stricter trust (matching what the maintained node-red fork
   for this device does - fingerprint pinning instead of date validation),
   this is the place to add a call that reads hCert's thumbprint and compares
   it against the known Velux cert thumbprint (028c23a0892b6298c499005bd2e72e0a703d716a,
   visible via 'cert-getcertlist' in the PLC Shell) instead of accepting
   unconditionally - but that requires confirming the correct system call
   for reading certificate properties from an RTS_IEC_HANDLE, which hasn't
   been verified yet.
------------------------------------------------------------------------ *)

FUNCTION_BLOCK FB_KLF200_CertVerifier IMPLEMENTS NBS.ICertificateVerifier
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
    // Debug instrumentation - confirms whether/how often this callback is
    // actually invoked, and with what eCurState each time. Inspect via a
    // WATCH (not a breakpoint - this runs from a system/comms task and
    // pausing it mid-handshake can itself cause a timeout on the KLF200
    // side).
    udiCallCount  : UDINT;
    aeCurStateLog : ARRAY[0..9] OF NBS.RTS_IEC_RESULT;
END_VAR
</Single>
            <Single Name="LineInfoPersistence" Type="string">6c08f547-ec6c-433e-9a64-8db562df68d0_Decl_LineIds</Single>
          </Single>
        </Single>
        <Single Name="UniqueIdGenerator" Type="string">93</Single>
        <Single Name="POULevel" Type="{8e575c5b-1d37-49c6-941b-5c0ec7874787}">Standard</Single>
        <List Name="ChildObjectGuids" Type="System.Collections.ArrayList" />
        <Single Name="AddAttributeSubsequent" Type="bool">False</Single>
      </Single>
      <Single Name="ParentSVNodeGuid" Type="System.Guid">76689472-8b42-4026-9ac6-2510a39dcddb</Single>
      <Array Name="Path" Type="string">
        <Single Type="string">Device</Single>
        <Single Type="string">PLC Logic</Single>
        <Single Type="string">Application</Single>
        <Single Type="string">KLF200</Single>
      </Array>
      <Single Name="Index" Type="int">-1</Single>
    </Single>
    <Single Type="{6198ad31-4b98-445c-927f-3258a0e82fe3}" Method="IArchivable">
      <Single Name="IsRoot" Type="bool">False</Single>
      <Single Name="MetaObject" Type="{81297157-7ec9-45ce-845e-84cab2b88ade}" Method="IArchivable">
        <Single Name="Guid" Type="System.Guid">bb59131a-0f71-4d6c-b55f-d4b23a080f3e</Single>
        <Single Name="ParentGuid" Type="System.Guid">05d070a2-43cc-48d2-8104-cb7dcf9f2249</Single>
        <Single Name="Name" Type="string">FB_KLF200_Client</Single>
        <Dictionary Type="{2c41fa04-1834-41c1-816e-303c7aa2c05b}" Name="Properties">
          <Entry>
            <Key>
              <Single Type="System.Guid">24568a24-c491-472c-a21f-ee5d33859fab</Single>
            </Key>
            <Value>
              <Single Type="{24568a24-c491-472c-a21f-ee5d33859fab}" Method="IArchivable">
                <Single Name="MemoryReserveForOnlineChange" Type="int">0</Single>
                <Single Name="ExcludeFromBuild" Type="bool">False</Single>
                <Single Name="External" Type="bool">False</Single>
                <Single Name="EnableSystemCall" Type="bool">False</Single>
                <Single Name="CompilerDefines" Type="string"></Single>
                <Single Name="LinkAlways" Type="bool">False</Single>
                <Array Name="Undefines" Type="string" />
              </Single>
            </Value>
          </Entry>
          <Entry>
            <Key>
              <Single Type="System.Guid">829a18f2-c514-4f6e-9634-1df173429203</Single>
            </Key>
            <Value>
              <Single Type="{829a18f2-c514-4f6e-9634-1df173429203}" Method="IArchivable">
                <Dictionary Type="{fa2ee218-a39b-4b6d-b249-49dbddbd168a}" Name="ParentObjects">
                  <Entry>
                    <Key>
                      <Single Type="System.Guid">d9b2b2cc-ea99-4c3b-aa42-1e5c49e65b84</Single>
                    </Key>
                    <Value>
                      <Single Type="System.Guid">76689472-8b42-4026-9ac6-2510a39dcddb</Single>
                    </Value>
                  </Entry>
                </Dictionary>
              </Single>
            </Value>
          </Entry>
        </Dictionary>
        <Single Name="TypeGuid" Type="System.Guid">6f9dac99-8de1-4efc-8465-68ac443b7d08</Single>
        <Array Name="EmbeddedTypeGuids" Type="System.Guid">
          <Single Type="System.Guid">a9ed5b7e-75c5-4651-af16-d2c27e98cb94</Single>
          <Single Type="System.Guid">3b83b776-fb25-43b8-99f2-3c507c9143fc</Single>
        </Array>
        <Single Name="Timestamp" Type="long">639206621735918221</Single>
      </Single>
      <Single Name="Object" Type="{6f9dac99-8de1-4efc-8465-68ac443b7d08}" Method="IArchivable">
        <Single Name="SpecialFunc" Type="{0db3d7bb-cde0-4416-9a7b-ce49a0124323}">None</Single>
        <Single Name="Implementation" Type="{3b83b776-fb25-43b8-99f2-3c507c9143fc}" Method="IArchivable">
          <Single Name="TextDocument" Type="{f3878285-8e4f-490b-bb1b-9acbb7eb04db}" Method="IArchivable">
            <Single Name="TextBlobForSerialisation" Type="string">// ---- edge detect start ----
IF xStart AND NOT xStartLast THEN
    eState := ST_CONNECTING;
    xEnableTcp := TRUE;
    xDone := FALSE;
    xAuthOk := FALSE;
    xError := FALSE;
    sErrorMsg := '';
END_IF
xStartLast := xStart;

xBusy := (eState &lt;&gt; ST_IDLE) AND (eState &lt;&gt; ST_DONE) AND (eState &lt;&gt; ST_ERROR);

// gvlKLF200Conn.tcpClient must be called every cycle once enabled, regardless of state,
// or the connection will never progress / stay serviced.
gvlKLF200Conn.tcpClient(
    xEnable       := xEnableTcp,
    itfTLSContext := gvlKLF200Conn.tlsContext,
    itfIPAddress  := gvlKLF200Conn.ipAddress,
    uiPort        := gvlKLF200.KLF200_PORT,
    udiTimeOut    := 10_000_000 // 10s connect timeout, in microseconds
);

CASE eState OF

ST_IDLE:
    ; // wait for xStart

ST_CONNECTING:
    tonTimeout(IN := TRUE, PT := T#10S);
    eState := ST_WAIT_CONNECTED;

ST_WAIT_CONNECTED:
    tonTimeout(IN := TRUE, PT := T#10S);

    IF gvlKLF200Conn.tcpClient.xActive THEN
        tonTimeout(IN := FALSE);
        tonTimeout(IN := TRUE, PT := T#10S); // fresh timer for the TLS handshake itself
        eState := ST_TLS_UPGRADE;
    ELSIF gvlKLF200Conn.tcpClient.xError OR tonTimeout.Q THEN
        tonTimeout(IN := FALSE);
        xError := TRUE;
        sErrorMsg := 'TCP/TLS connect failed or timed out';
        eState := ST_ERROR;
    END_IF

ST_TLS_UPGRADE:
    tonTimeout(IN := TRUE, PT := T#10S);

    // xActive going TRUE in ST_WAIT_CONNECTED appears to reflect the raw TCP
    // socket only, not a completed TLS handshake (confirmed by packet
    // capture: data leaves in plaintext the instant xActive is true, with
    // zero TLS records ever seen). Explicitly gating on Upgrade() here,
    // in addition to (not instead of) the itfTLSContext wiring at
    // declaration - testing both together, since neither alone has worked.
    eUpgradeErr := gvlKLF200Conn.tcpClient.Upgrade();

    IF eUpgradeErr = NBS.ERROR.NO_ERROR THEN
        tonTimeout(IN := FALSE);
        eState := ST_SEND_PASSWORD;
    ELSIF eUpgradeErr &lt;&gt; NBS.ERROR.PENDING THEN
        xError := TRUE;
        sErrorMsg := 'TLS upgrade failed';
        eState := ST_ERROR;
    END_IF;

    IF (eState = ST_TLS_UPGRADE) AND tonTimeout.Q THEN
        tonTimeout(IN := FALSE);
        xError := TRUE;
        sErrorMsg := 'TLS upgrade timed out';
        eState := ST_ERROR;
    END_IF

ST_SEND_PASSWORD:
    // --- build GW_PASSWORD_ENTER_REQ command frame: Command(2) + Password(32) ---
    FOR i := 0 TO gvlKLF200.MAX_FRAME_LEN - 1 DO
        abyCmdFrame[i] := 0;
    END_FOR

    abyCmdFrame[0] := WORD_TO_BYTE(SHR(gvlKLF200.CMD_PASSWORD_ENTER_REQ, 8)); // command, big-endian
    abyCmdFrame[1] := WORD_TO_BYTE(gvlKLF200.CMD_PASSWORD_ENTER_REQ AND 16#FF);

    // password bytes, null-padded, 32 bytes total for the data field.
    // CODESYS STRING variables are plain null-terminated CHAR arrays in memory,
    // so a straight byte copy of LEN() bytes from ADR() is correct here.
    // SysMemCpy is from the SysMem library - add it via Library Manager if not
    // already referenced.
    SysMem.SysMemCpy(
        pDest    := ADR(abyCmdFrame) + 2,
        pSrc     := ADR(gvlKLF200.KLF200_PASSWORD),
        udiCount := TO_UDINT(LEN(gvlKLF200.KLF200_PASSWORD))
    );
    udiCmdLen := 2 + 32; // Command + 32-byte password field

    // --- transport layer: ProtocolID(0x00) + Length + CmdFrame + Checksum ---
    abyTransFrame[0] := 16#00;                    // ProtocolID
    // Length field = itself(1) + Command(2) + Data(n), per spec 4.3.1 / Figure 2
    // (Length=3 for "no data" confirms it counts the Length byte itself)
    abyTransFrame[1] := UDINT_TO_BYTE(udiCmdLen + 1);
    FOR i := 0 TO udiCmdLen - 1 DO
        abyTransFrame[2 + i] := abyCmdFrame[i];
    END_FOR
    udiTransLen := 2 + udiCmdLen; // ProtocolID+Length+Cmd+Data, checksum not yet appended

    byChecksumCalc := FUN_XorChecksum(ADR(abyTransFrame), udiTransLen);
    abyTransFrame[udiTransLen] := byChecksumCalc;
    udiTransLen := udiTransLen + 1;

    // --- SLIP wrap ---
    udiSlipOutLen := FUN_SlipEncode(ADR(abyTransFrame), udiTransLen, ADR(abySlipOut), gvlKLF200.MAX_SLIP_LEN);

    IF udiSlipOutLen = 0 THEN
        xError := TRUE;
        sErrorMsg := 'SLIP encode failed (buffer too small?)';
        eState := ST_ERROR;
    ELSE
        // *** ASSUMPTION - VERIFY ***: Write() likely follows the same async
        // pattern as Read() (which you confirmed returns PENDING while a
        // scan hasn't completed the operation yet). If so, a single call
        // here can't guarantee the bytes were actually sent - it may just
        // have started the transfer. So: attempt once here, and if it comes
        // back PENDING, move to ST_WAIT_WRITE and keep calling Write() with
        // the SAME buffer/size each scan until it reports NO_ERROR (done)
        // or a real error. This mirrors the Read() polling pattern, but I
        // haven't confirmed NBS's exact re-entry semantics for a pending
        // Write() call - check the docs/IntelliSense if this doesn't behave
        // as expected (e.g. if it turns out you must NOT re-pass pData/
        // udiSize on a retry).
        eWriteErr := gvlKLF200Conn.tcpClient.Write(
            pData    := ADR(abySlipOut),
            udiSize  := udiSlipOutLen,
            udiCount =&gt; udiWritten
        );

        IF eWriteErr = NBS.ERROR.NO_ERROR THEN
            tonTimeout(IN := TRUE, PT := T#5S);
            eState := ST_WAIT_RESPONSE;
        ELSIF eWriteErr = NBS.ERROR.PENDING THEN
            tonTimeout(IN := TRUE, PT := T#5S);
            eState := ST_WAIT_WRITE;
        ELSE
            xError := TRUE;
            sErrorMsg := 'TCP write failed';
            eState := ST_ERROR;
        END_IF;
    END_IF;

ST_WAIT_WRITE:
    tonTimeout(IN := TRUE, PT := T#5S);

    eWriteErr := gvlKLF200Conn.tcpClient.Write(
        pData    := ADR(abySlipOut),
        udiSize  := udiSlipOutLen,
        udiCount =&gt; udiWritten
    );

    IF eWriteErr = NBS.ERROR.NO_ERROR THEN
        tonTimeout(IN := TRUE, PT := T#5S); // restart timer fresh for the response wait
        eState := ST_WAIT_RESPONSE;
    ELSIF eWriteErr &lt;&gt; NBS.ERROR.PENDING THEN
        xError := TRUE;
        sErrorMsg := 'TCP write failed';
        eState := ST_ERROR;
    END_IF;

    IF (eState = ST_WAIT_WRITE) AND tonTimeout.Q THEN
        tonTimeout(IN := FALSE);
        xError := TRUE;
        sErrorMsg := 'TCP write did not complete (timeout)';
        eState := ST_ERROR;
    END_IF

ST_WAIT_RESPONSE:
    tonTimeout(IN := TRUE, PT := T#5S);

    // NBS.ERROR.PENDING = no data available yet this scan (normal while
    // polling every cycle) - not a real error.
    eReadErr := gvlKLF200Conn.tcpClient.Read(
        pData    := ADR(abyRxRaw),
        udiSize  := gvlKLF200.MAX_SLIP_LEN,
        udiCount =&gt; udiRxRawLen
    );

    IF eReadErr &lt;&gt; NBS.ERROR.NO_ERROR AND eReadErr &lt;&gt; NBS.ERROR.PENDING THEN
        xError := TRUE;
        sErrorMsg := 'TCP read failed';
        eState := ST_ERROR;
    ELSIF eReadErr = NBS.ERROR.NO_ERROR AND udiRxRawLen &gt; 2 THEN
        // strip leading/trailing SLIP END (0xC0) before decoding
        udiRxDecodedLen := FUN_SlipDecode(
            ADR(abyRxRaw) + 1,
            udiRxRawLen - 2,
            ADR(abyRxDecoded),
            SIZEOF(abyRxDecoded)
        );

        IF udiRxDecodedLen &gt;= 5 THEN // ProtocolID+Length+Cmd(2)+Status(1)+Checksum, minimum
            wRxCommand := SHL(BYTE_TO_WORD(abyRxDecoded[2]), 8) OR BYTE_TO_WORD(abyRxDecoded[3]);
            byRxStatus := abyRxDecoded[4];

            IF wRxCommand = gvlKLF200.CMD_PASSWORD_ENTER_CFM THEN
                xAuthOk := (byRxStatus = 0);
                xDone := TRUE;
                tonTimeout(IN := FALSE);
                eState := ST_DONE;
            ELSE
                xError := TRUE;
                sErrorMsg := 'Unexpected response command';
                eState := ST_ERROR;
            END_IF
        ELSE
            xError := TRUE;
            sErrorMsg := 'SLIP decode failed / short frame';
            eState := ST_ERROR;
        END_IF
    END_IF;

    // Timeout check is independent of the read-result branching above, so
    // repeated PENDING reads (the normal case while waiting) don't mask it.
    // eState may have just been set to ST_DONE/ST_ERROR above, in which case
    // this is harmlessly skipped for the remainder of THIS scan (the CASE
    // dispatch uses the old eState until the next scan regardless).
    IF (eState = ST_WAIT_RESPONSE) AND tonTimeout.Q THEN
        tonTimeout(IN := FALSE);
        xError := TRUE;
        sErrorMsg := 'No response to GW_PASSWORD_ENTER_REQ (timeout)';
        eState := ST_ERROR;
    END_IF

ST_DONE:
    ; // stay here; xDone/xAuthOk hold the result until xStart is retriggered

ST_ERROR:
    ; // stay here; sErrorMsg holds the reason until xStart is retriggered

END_CASE
</Single>
            <Single Name="LineInfoPersistence" Type="string">bb59131a-0f71-4d6c-b55f-d4b23a080f3e_Impl_LineIds</Single>
          </Single>
        </Single>
        <Single Name="Interface" Type="{a9ed5b7e-75c5-4651-af16-d2c27e98cb94}" Method="IArchivable">
          <Single Name="TextDocument" Type="{f3878285-8e4f-490b-bb1b-9acbb7eb04db}" Method="IArchivable">
            <Single Name="TextBlobForSerialisation" Type="string">// ------------------------------------------------------------------------
//   FB_KLF200_Client - top-level state machine
// ------------------------------------------------------------------------ 
FUNCTION_BLOCK FB_KLF200_Client
VAR_INPUT
    xStart : BOOL; // rising edge starts connect+auth sequence
END_VAR
VAR_OUTPUT
    xBusy       : BOOL;
    xDone       : BOOL;   // TRUE once password CFM received (regardless of status)
    xAuthOk     : BOOL;   // TRUE once CFM Status = 0
    xError      : BOOL;
    sErrorMsg   : STRING(80);
END_VAR
VAR
    eState : (
        ST_IDLE,
        ST_CONNECTING,
        ST_WAIT_CONNECTED,
        ST_TLS_UPGRADE,
        ST_SEND_PASSWORD,
        ST_WAIT_WRITE,
        ST_WAIT_RESPONSE,
        ST_DONE,
        ST_ERROR
    ) := ST_IDLE;

    xStartLast : BOOL;

    // ipAddress / tlsContext / tcpClient now live in gvlKLF200Conn (see that
    // GVL's header comment for why) - referenced directly below, not
    // declared here.
    xEnableTcp  : BOOL;

    // --- frame buffers ------------------------------------------------
    abyCmdFrame   : ARRAY[0..gvlKLF200.MAX_FRAME_LEN-1] OF BYTE;   // Command(2) + Data
    udiCmdLen     : UDINT;

    abyTransFrame : ARRAY[0..gvlKLF200.MAX_FRAME_LEN+1] OF BYTE;   // ProtocolID+Length+Cmd+Data+Checksum
    udiTransLen   : UDINT;

    abySlipOut    : ARRAY[0..gvlKLF200.MAX_SLIP_LEN-1] OF BYTE;
    udiSlipOutLen : UDINT;

    abyRxRaw      : ARRAY[0..gvlKLF200.MAX_SLIP_LEN-1] OF BYTE;    // raw bytes off the socket
    udiRxRawLen   : UDINT;

    abyRxDecoded  : ARRAY[0..gvlKLF200.MAX_FRAME_LEN+1] OF BYTE;   // after SLIP decode
    udiRxDecodedLen : UDINT;

    i : UDINT;
    byChecksumCalc : BYTE;
    wRxCommand : WORD;
    byRxStatus : BYTE;

    eWriteErr   : NBS.ERROR;
    eUpgradeErr : NBS.ERROR;
    udiWritten  : UDINT;
    eReadErr    : NBS.ERROR;

    tonTimeout : TON;

END_VAR
</Single>
            <Single Name="LineInfoPersistence" Type="string">bb59131a-0f71-4d6c-b55f-d4b23a080f3e_Decl_LineIds</Single>
          </Single>
        </Single>
        <Single Name="UniqueIdGenerator" Type="string">3126</Single>
        <Single Name="POULevel" Type="{8e575c5b-1d37-49c6-941b-5c0ec7874787}">Standard</Single>
        <List Name="ChildObjectGuids" Type="System.Collections.ArrayList" />
        <Single Name="AddAttributeSubsequent" Type="bool">False</Single>
      </Single>
      <Single Name="ParentSVNodeGuid" Type="System.Guid">76689472-8b42-4026-9ac6-2510a39dcddb</Single>
      <Array Name="Path" Type="string">
        <Single Type="string">Device</Single>
        <Single Type="string">PLC Logic</Single>
        <Single Type="string">Application</Single>
        <Single Type="string">KLF200</Single>
      </Array>
      <Single Name="Index" Type="int">-1</Single>
    </Single>
    <Single Type="{6198ad31-4b98-445c-927f-3258a0e82fe3}" Method="IArchivable">
      <Single Name="IsRoot" Type="bool">False</Single>
      <Single Name="MetaObject" Type="{81297157-7ec9-45ce-845e-84cab2b88ade}" Method="IArchivable">
        <Single Name="Guid" Type="System.Guid">91ffe081-816d-454b-ac02-da4e8f1270ad</Single>
        <Single Name="ParentGuid" Type="System.Guid">05d070a2-43cc-48d2-8104-cb7dcf9f2249</Single>
        <Single Name="Name" Type="string">FUN_SlipDecode</Single>
        <Dictionary Type="{2c41fa04-1834-41c1-816e-303c7aa2c05b}" Name="Properties">
          <Entry>
            <Key>
              <Single Type="System.Guid">829a18f2-c514-4f6e-9634-1df173429203</Single>
            </Key>
            <Value>
              <Single Type="{829a18f2-c514-4f6e-9634-1df173429203}" Method="IArchivable">
                <Dictionary Type="{fa2ee218-a39b-4b6d-b249-49dbddbd168a}" Name="ParentObjects">
                  <Entry>
                    <Key>
                      <Single Type="System.Guid">d9b2b2cc-ea99-4c3b-aa42-1e5c49e65b84</Single>
                    </Key>
                    <Value>
                      <Single Type="System.Guid">76689472-8b42-4026-9ac6-2510a39dcddb</Single>
                    </Value>
                  </Entry>
                </Dictionary>
              </Single>
            </Value>
          </Entry>
        </Dictionary>
        <Single Name="TypeGuid" Type="System.Guid">6f9dac99-8de1-4efc-8465-68ac443b7d08</Single>
        <Array Name="EmbeddedTypeGuids" Type="System.Guid">
          <Single Type="System.Guid">a9ed5b7e-75c5-4651-af16-d2c27e98cb94</Single>
          <Single Type="System.Guid">3b83b776-fb25-43b8-99f2-3c507c9143fc</Single>
        </Array>
        <Single Name="Timestamp" Type="long">639204336334745931</Single>
      </Single>
      <Single Name="Object" Type="{6f9dac99-8de1-4efc-8465-68ac443b7d08}" Method="IArchivable">
        <Single Name="SpecialFunc" Type="{0db3d7bb-cde0-4416-9a7b-ce49a0124323}">None</Single>
        <Single Name="Implementation" Type="{3b83b776-fb25-43b8-99f2-3c507c9143fc}" Method="IArchivable">
          <Single Name="TextDocument" Type="{f3878285-8e4f-490b-bb1b-9acbb7eb04db}" Method="IArchivable">
            <Single Name="TextBlobForSerialisation" Type="string">WHILE i &lt; udiInLen DO
    pCur := pbyIn + i;
    byB := pCur^;

    IF byB = bESC THEN
        i := i + 1;
        IF i &gt;= udiInLen THEN FUN_SlipDecode := 0; RETURN; END_IF
        pCur := pbyIn + i;
        byB := pCur^;
        IF byB = bESCEND THEN
            byB := bEND;
        ELSIF byB = bESCESC THEN
            byB := bESC;
        ELSE
            FUN_SlipDecode := 0; RETURN; // invalid escape sequence
        END_IF
    END_IF

    IF udiPos &gt;= udiOutMaxLen THEN FUN_SlipDecode := 0; RETURN; END_IF
    pCur := pbyOut + udiPos;
    pCur^ := byB;
    udiPos := udiPos + 1;
    i := i + 1;
END_WHILE

FUN_SlipDecode := udiPos;

</Single>
            <Single Name="LineInfoPersistence" Type="string">91ffe081-816d-454b-ac02-da4e8f1270ad_Impl_LineIds</Single>
          </Single>
        </Single>
        <Single Name="Interface" Type="{a9ed5b7e-75c5-4651-af16-d2c27e98cb94}" Method="IArchivable">
          <Single Name="TextDocument" Type="{f3878285-8e4f-490b-bb1b-9acbb7eb04db}" Method="IArchivable">
            <Single Name="TextBlobForSerialisation" Type="string">// ------------------------------------------------------------------------
//   FUN_SlipDecode - unwraps one SLIP frame (leading/trailing 0xC0 stripped).
//   Returns number of decoded bytes written to pbyOut, or 0 on error/overflow.
//   Expects pbyIn to point at the byte AFTER the leading END marker, and
//   udiInLen to be the count up to (not including) the trailing END marker.
// ------------------------------------------------------------------------
FUNCTION FUN_SlipDecode : UDINT
VAR_INPUT
    pbyIn        : POINTER TO BYTE;
    udiInLen     : UDINT;
    pbyOut       : POINTER TO BYTE;
    udiOutMaxLen : UDINT;
END_VAR
VAR
    i      : UDINT := 0;
    udiPos : UDINT := 0;
    byB    : BYTE;
    pCur   : POINTER TO BYTE;
END_VAR
VAR CONSTANT
    // local copies of GVL constants for readability
    bEND     : BYTE := 16#C0;
    bESC     : BYTE := 16#DB;
    bESCEND  : BYTE := 16#DC;
    bESCESC  : BYTE := 16#DD;
END_VAR
</Single>
            <Single Name="LineInfoPersistence" Type="string">91ffe081-816d-454b-ac02-da4e8f1270ad_Decl_LineIds</Single>
          </Single>
        </Single>
        <Single Name="UniqueIdGenerator" Type="string">55</Single>
        <Single Name="POULevel" Type="{8e575c5b-1d37-49c6-941b-5c0ec7874787}">Standard</Single>
        <List Name="ChildObjectGuids" Type="System.Collections.ArrayList" />
        <Single Name="AddAttributeSubsequent" Type="bool">False</Single>
      </Single>
      <Single Name="ParentSVNodeGuid" Type="System.Guid">76689472-8b42-4026-9ac6-2510a39dcddb</Single>
      <Array Name="Path" Type="string">
        <Single Type="string">Device</Single>
        <Single Type="string">PLC Logic</Single>
        <Single Type="string">Application</Single>
        <Single Type="string">KLF200</Single>
      </Array>
      <Single Name="Index" Type="int">-1</Single>
    </Single>
    <Single Type="{6198ad31-4b98-445c-927f-3258a0e82fe3}" Method="IArchivable">
      <Single Name="IsRoot" Type="bool">False</Single>
      <Single Name="MetaObject" Type="{81297157-7ec9-45ce-845e-84cab2b88ade}" Method="IArchivable">
        <Single Name="Guid" Type="System.Guid">6bfc0ef0-abb1-405a-8cdb-3d18e8adc0f2</Single>
        <Single Name="ParentGuid" Type="System.Guid">05d070a2-43cc-48d2-8104-cb7dcf9f2249</Single>
        <Single Name="Name" Type="string">FUN_SlipEncode</Single>
        <Dictionary Type="{2c41fa04-1834-41c1-816e-303c7aa2c05b}" Name="Properties">
          <Entry>
            <Key>
              <Single Type="System.Guid">829a18f2-c514-4f6e-9634-1df173429203</Single>
            </Key>
            <Value>
              <Single Type="{829a18f2-c514-4f6e-9634-1df173429203}" Method="IArchivable">
                <Dictionary Type="{fa2ee218-a39b-4b6d-b249-49dbddbd168a}" Name="ParentObjects">
                  <Entry>
                    <Key>
                      <Single Type="System.Guid">d9b2b2cc-ea99-4c3b-aa42-1e5c49e65b84</Single>
                    </Key>
                    <Value>
                      <Single Type="System.Guid">76689472-8b42-4026-9ac6-2510a39dcddb</Single>
                    </Value>
                  </Entry>
                </Dictionary>
              </Single>
            </Value>
          </Entry>
        </Dictionary>
        <Single Name="TypeGuid" Type="System.Guid">6f9dac99-8de1-4efc-8465-68ac443b7d08</Single>
        <Array Name="EmbeddedTypeGuids" Type="System.Guid">
          <Single Type="System.Guid">a9ed5b7e-75c5-4651-af16-d2c27e98cb94</Single>
          <Single Type="System.Guid">3b83b776-fb25-43b8-99f2-3c507c9143fc</Single>
        </Array>
        <Single Name="Timestamp" Type="long">639204336425007688</Single>
      </Single>
      <Single Name="Object" Type="{6f9dac99-8de1-4efc-8465-68ac443b7d08}" Method="IArchivable">
        <Single Name="SpecialFunc" Type="{0db3d7bb-cde0-4416-9a7b-ce49a0124323}">None</Single>
        <Single Name="Implementation" Type="{3b83b776-fb25-43b8-99f2-3c507c9143fc}" Method="IArchivable">
          <Single Name="TextDocument" Type="{f3878285-8e4f-490b-bb1b-9acbb7eb04db}" Method="IArchivable">
            <Single Name="TextBlobForSerialisation" Type="string">
// leading END
IF udiPos &gt;= udiOutMaxLen THEN
	FUN_SlipEncode := 0;
	RETURN;
END_IF

pCur := pbyOut + udiPos;
pCur^ := bEND;
udiPos := udiPos + 1;

FOR i := 0 TO udiInLen - 1 DO
    pCur := pbyIn + i;
    byB := pCur^;

    IF byB = bEND THEN
        IF udiPos + 2 &gt; udiOutMaxLen THEN FUN_SlipEncode := 0; RETURN; END_IF
        pCur := pbyOut + udiPos; pCur^ := bESC;     udiPos := udiPos + 1;
        pCur := pbyOut + udiPos; pCur^ := bESCEND;  udiPos := udiPos + 1;
    ELSIF byB = bESC THEN
        IF udiPos + 2 &gt; udiOutMaxLen THEN FUN_SlipEncode := 0; RETURN; END_IF
        pCur := pbyOut + udiPos; pCur^ := bESC;     udiPos := udiPos + 1;
        pCur := pbyOut + udiPos; pCur^ := bESCESC;  udiPos := udiPos + 1;
    ELSE
        IF udiPos + 1 &gt; udiOutMaxLen THEN FUN_SlipEncode := 0; RETURN; END_IF
        pCur := pbyOut + udiPos; pCur^ := byB;      udiPos := udiPos + 1;
    END_IF
END_FOR

// trailing END
IF udiPos &gt;= udiOutMaxLen THEN
	FUN_SlipEncode := 0;
	RETURN;
END_IF
pCur := pbyOut + udiPos;
pCur^ := bEND;
udiPos := udiPos + 1;

FUN_SlipEncode := udiPos;
</Single>
            <Single Name="LineInfoPersistence" Type="string">6bfc0ef0-abb1-405a-8cdb-3d18e8adc0f2_Impl_LineIds</Single>
          </Single>
        </Single>
        <Single Name="Interface" Type="{a9ed5b7e-75c5-4651-af16-d2c27e98cb94}" Method="IArchivable">
          <Single Name="TextDocument" Type="{f3878285-8e4f-490b-bb1b-9acbb7eb04db}" Method="IArchivable">
            <Single Name="TextBlobForSerialisation" Type="string">// ------------------------------------------------------------------------
//   FUN_SlipEncode - wraps a transport-layer frame into a SLIP frame.
//   Returns the number of bytes written to pbyOut, or 0 on overflow.
// ------------------------------------------------------------------------
FUNCTION FUN_SlipEncode : UDINT
VAR_INPUT
    pbyIn:			POINTER TO BYTE;
    udiInLen:		UDINT;
    pbyOut:			POINTER TO BYTE;
    udiOutMaxLen:	UDINT;
END_VAR
VAR
    i:				UDINT;
    udiPos: 		UDINT := 0;
    byB:			BYTE;
    pCur:			POINTER TO BYTE;
END_VAR
VAR CONSTANT
    // local copies of GVL constants for readability
    bEND     : BYTE := 16#C0;
    bESC     : BYTE := 16#DB;
    bESCEND  : BYTE := 16#DC;
    bESCESC  : BYTE := 16#DD;
END_VAR</Single>
            <Single Name="LineInfoPersistence" Type="string">6bfc0ef0-abb1-405a-8cdb-3d18e8adc0f2_Decl_LineIds</Single>
          </Single>
        </Single>
        <Single Name="UniqueIdGenerator" Type="string">90</Single>
        <Single Name="POULevel" Type="{8e575c5b-1d37-49c6-941b-5c0ec7874787}">Standard</Single>
        <List Name="ChildObjectGuids" Type="System.Collections.ArrayList" />
        <Single Name="AddAttributeSubsequent" Type="bool">False</Single>
      </Single>
      <Single Name="ParentSVNodeGuid" Type="System.Guid">76689472-8b42-4026-9ac6-2510a39dcddb</Single>
      <Array Name="Path" Type="string">
        <Single Type="string">Device</Single>
        <Single Type="string">PLC Logic</Single>
        <Single Type="string">Application</Single>
        <Single Type="string">KLF200</Single>
      </Array>
      <Single Name="Index" Type="int">-1</Single>
    </Single>
    <Single Type="{6198ad31-4b98-445c-927f-3258a0e82fe3}" Method="IArchivable">
      <Single Name="IsRoot" Type="bool">False</Single>
      <Single Name="MetaObject" Type="{81297157-7ec9-45ce-845e-84cab2b88ade}" Method="IArchivable">
        <Single Name="Guid" Type="System.Guid">d81b2ffd-d70c-46f5-b138-b52a93b460b6</Single>
        <Single Name="ParentGuid" Type="System.Guid">05d070a2-43cc-48d2-8104-cb7dcf9f2249</Single>
        <Single Name="Name" Type="string">FUN_XorChecksum</Single>
        <Dictionary Type="{2c41fa04-1834-41c1-816e-303c7aa2c05b}" Name="Properties">
          <Entry>
            <Key>
              <Single Type="System.Guid">829a18f2-c514-4f6e-9634-1df173429203</Single>
            </Key>
            <Value>
              <Single Type="{829a18f2-c514-4f6e-9634-1df173429203}" Method="IArchivable">
                <Dictionary Type="{fa2ee218-a39b-4b6d-b249-49dbddbd168a}" Name="ParentObjects">
                  <Entry>
                    <Key>
                      <Single Type="System.Guid">d9b2b2cc-ea99-4c3b-aa42-1e5c49e65b84</Single>
                    </Key>
                    <Value>
                      <Single Type="System.Guid">76689472-8b42-4026-9ac6-2510a39dcddb</Single>
                    </Value>
                  </Entry>
                </Dictionary>
              </Single>
            </Value>
          </Entry>
        </Dictionary>
        <Single Name="TypeGuid" Type="System.Guid">6f9dac99-8de1-4efc-8465-68ac443b7d08</Single>
        <Array Name="EmbeddedTypeGuids" Type="System.Guid">
          <Single Type="System.Guid">a9ed5b7e-75c5-4651-af16-d2c27e98cb94</Single>
          <Single Type="System.Guid">3b83b776-fb25-43b8-99f2-3c507c9143fc</Single>
        </Array>
        <Single Name="Timestamp" Type="long">639204336667001732</Single>
      </Single>
      <Single Name="Object" Type="{6f9dac99-8de1-4efc-8465-68ac443b7d08}" Method="IArchivable">
        <Single Name="SpecialFunc" Type="{0db3d7bb-cde0-4416-9a7b-ce49a0124323}">None</Single>
        <Single Name="Implementation" Type="{3b83b776-fb25-43b8-99f2-3c507c9143fc}" Method="IArchivable">
          <Single Name="TextDocument" Type="{f3878285-8e4f-490b-bb1b-9acbb7eb04db}" Method="IArchivable">
            <Single Name="TextBlobForSerialisation" Type="string">FOR i := 0 TO udiLen - 1 DO
    pCur := pbyData + i;
    byResult := byResult XOR pCur^;
END_FOR

FUN_XorChecksum := byResult;
</Single>
            <Single Name="LineInfoPersistence" Type="string">d81b2ffd-d70c-46f5-b138-b52a93b460b6_Impl_LineIds</Single>
          </Single>
        </Single>
        <Single Name="Interface" Type="{a9ed5b7e-75c5-4651-af16-d2c27e98cb94}" Method="IArchivable">
          <Single Name="TextDocument" Type="{f3878285-8e4f-490b-bb1b-9acbb7eb04db}" Method="IArchivable">
            <Single Name="TextBlobForSerialisation" Type="string">// ------------------------------------------------------------------------
//   FUN_XorChecksum - XOR all bytes in a buffer (transport layer checksum,
//   spec 4.4.2: XOR of ProtocolID..last data byte)
// ------------------------------------------------------------------------ 
FUNCTION FUN_XorChecksum : BYTE
VAR_INPUT
    pbyData:	POINTER TO BYTE;
    udiLen:		UDINT;
END_VAR
VAR
    i   : UDINT;
    byResult : BYTE := 0;
    pCur : POINTER TO BYTE;
END_VAR
</Single>
            <Single Name="LineInfoPersistence" Type="string">d81b2ffd-d70c-46f5-b138-b52a93b460b6_Decl_LineIds</Single>
          </Single>
        </Single>
        <Single Name="UniqueIdGenerator" Type="string">33</Single>
        <Single Name="POULevel" Type="{8e575c5b-1d37-49c6-941b-5c0ec7874787}">Standard</Single>
        <List Name="ChildObjectGuids" Type="System.Collections.ArrayList" />
        <Single Name="AddAttributeSubsequent" Type="bool">False</Single>
      </Single>
      <Single Name="ParentSVNodeGuid" Type="System.Guid">76689472-8b42-4026-9ac6-2510a39dcddb</Single>
      <Array Name="Path" Type="string">
        <Single Type="string">Device</Single>
        <Single Type="string">PLC Logic</Single>
        <Single Type="string">Application</Single>
        <Single Type="string">KLF200</Single>
      </Array>
      <Single Name="Index" Type="int">-1</Single>
    </Single>
    <Single Type="{6198ad31-4b98-445c-927f-3258a0e82fe3}" Method="IArchivable">
      <Single Name="IsRoot" Type="bool">False</Single>
      <Single Name="MetaObject" Type="{81297157-7ec9-45ce-845e-84cab2b88ade}" Method="IArchivable">
        <Single Name="Guid" Type="System.Guid">6d03627a-c95b-49dd-85ba-4b8a1a6601cb</Single>
        <Single Name="ParentGuid" Type="System.Guid">6c08f547-ec6c-433e-9a64-8db562df68d0</Single>
        <Single Name="Name" Type="string">VerifyCertificate</Single>
        <Dictionary Type="{2c41fa04-1834-41c1-816e-303c7aa2c05b}" Name="Properties" />
        <Single Name="TypeGuid" Type="System.Guid">f8a58466-d7f6-439f-bbb8-d4600e41d099</Single>
        <Array Name="EmbeddedTypeGuids" Type="System.Guid">
          <Single Type="System.Guid">a9ed5b7e-75c5-4651-af16-d2c27e98cb94</Single>
          <Single Type="System.Guid">3b83b776-fb25-43b8-99f2-3c507c9143fc</Single>
        </Array>
        <Single Name="Timestamp" Type="long">639206620165869184</Single>
      </Single>
      <Single Name="Object" Type="{f8a58466-d7f6-439f-bbb8-d4600e41d099}" Method="IArchivable">
        <Single Name="Implementation" Type="{3b83b776-fb25-43b8-99f2-3c507c9143fc}" Method="IArchivable">
          <Single Name="TextDocument" Type="{f3878285-8e4f-490b-bb1b-9acbb7eb04db}" Method="IArchivable">
            <Single Name="TextBlobForSerialisation" Type="string">
IF udiCallCount &lt; 10 THEN
    aeCurStateLog[udiCallCount] := eCurState;
END_IF


//debug information
udiCallCount := udiCallCount + 1;


// Accept unconditionally, overriding whatever the built-in check (eCurState)
// determined - specifically to bypass the expired-certificate rejection.
// *** VERIFY IN INTELLISENSE ***: I'm assuming 0 (ERR_OK, standard CODESYS
// system "success" convention) signals "accept" here, consistent with
// eCurState being typed RTS_IEC_RESULT (where 0 conventionally means no
// error). If the handshake still fails after wiring this in, check what
// RTS_IEC_RESULT / ERR_OK actually resolves to in your project (type
// "ERR_OK" or hover RTS_IEC_RESULT) to confirm the right "accept" value.
VerifyCertificate := eCurState;
</Single>
            <Single Name="LineInfoPersistence" Type="string">6d03627a-c95b-49dd-85ba-4b8a1a6601cb_Impl_LineIds</Single>
          </Single>
        </Single>
        <Single Name="Interface" Type="{a9ed5b7e-75c5-4651-af16-d2c27e98cb94}" Method="IArchivable">
          <Single Name="TextDocument" Type="{f3878285-8e4f-490b-bb1b-9acbb7eb04db}" Method="IArchivable">
            <Single Name="TextBlobForSerialisation" Type="string">METHOD VerifyCertificate : UDINT
VAR_INPUT
	hCert	: NBS.RTS_IEC_HANDLE;
	eCurState	: NBS.RTS_IEC_RESULT;
END_VAR
</Single>
            <Single Name="LineInfoPersistence" Type="string">6d03627a-c95b-49dd-85ba-4b8a1a6601cb_Decl_LineIds</Single>
          </Single>
        </Single>
      </Single>
      <Single Name="ParentSVNodeGuid" Type="System.Guid">6c08f547-ec6c-433e-9a64-8db562df68d0</Single>
      <Array Name="Path" Type="string">
        <Single Type="string">Device</Single>
        <Single Type="string">PLC Logic</Single>
        <Single Type="string">Application</Single>
        <Single Type="string">KLF200</Single>
        <Single Type="string">FB_KLF200_CertVerifier</Single>
      </Array>
      <Single Name="Index" Type="int">-1</Single>
    </Single>
  </List2>
  <Single Name="ProfileName" Type="string">CODESYS V3.5 SP20 Patch 3</Single>
</Single>  </StructuredView>
</ExportFile>
