You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2026/08/18 10:38:05 UTC
(plc4x) 08/09: feat(plc4go/s7): real round-trip ping
This is an automated email from the ASF dual-hosted git repository.
sruehl pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git
commit 203cfdd5ad5048b81b13f1fe125a5021e75c1121
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Tue Aug 18 12:17:25 2026 +0200
feat(plc4go/s7): real round-trip ping
The default ping only checked connection flags and couldn't detect a
half-open TCP connection or an unresponsive CPU. Port the plc4j approach:
a one-byte ReadVar of %M0 where any S7 answer counts as alive.
---
plc4go/internal/s7/Connection.go | 60 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)
diff --git a/plc4go/internal/s7/Connection.go b/plc4go/internal/s7/Connection.go
index 7f3589bb5a..9e0d293623 100644
--- a/plc4go/internal/s7/Connection.go
+++ b/plc4go/internal/s7/Connection.go
@@ -415,6 +415,66 @@ func (c *Connection) GetMetadata() apiModel.PlcConnectionMetadata {
}
}
+// Ping does a real round trip: S7 has no dedicated ping, so a one-byte read of %M0 is
+// issued and any S7 answer (including a PLC error) counts as alive - only transport
+// failures and timeouts report an error.
+func (c *Connection) Ping(ctx context.Context) error {
+ if !c.IsConnected() {
+ return errors.New("not connected")
+ }
+ tpduId := c.tpduGenerator.getAndIncrement()
+ pingRequest := readWriteModel.NewTPKTPacket(readWriteModel.NewCOTPPacketData(
+ nil,
+ readWriteModel.NewS7MessageRequest(
+ tpduId,
+ readWriteModel.NewS7ParameterReadVarRequest([]readWriteModel.S7VarRequestParameterItem{
+ readWriteModel.NewS7VarRequestParameterItemAddress(
+ readWriteModel.NewS7AddressAny(readWriteModel.TransportSize_BYTE, 1, 0, readWriteModel.MemoryArea_FLAGS_MARKERS, 0, 0),
+ ),
+ }),
+ nil,
+ ),
+ true,
+ uint8(tpduId),
+ ))
+ successChan := make(chan struct{}, 1)
+ errChan := make(chan error, 1)
+ if err := c.messageCodec.SendRequest(ctx, "ping", pingRequest, func(message spi.Message) bool {
+ tpktPacket, ok := message.(readWriteModel.TPKTPacket)
+ if !ok {
+ return false
+ }
+ cotpPacketData, ok := tpktPacket.GetPayload().(readWriteModel.COTPPacketData)
+ if !ok {
+ return false
+ }
+ payload := cotpPacketData.GetPayload()
+ return payload != nil && payload.GetTpduReference() == tpduId
+ }, func(message spi.Message) error {
+ select {
+ case successChan <- struct{}{}:
+ default:
+ }
+ return nil
+ }, func(err error) error {
+ select {
+ case errChan <- err:
+ default:
+ }
+ return nil
+ }); err != nil {
+ return errors.Wrap(err, "error sending ping request")
+ }
+ select {
+ case <-successChan:
+ return nil
+ case err := <-errChan:
+ return errors.Wrap(err, "got error while waiting for ping response")
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+}
+
func (c *Connection) dispatchUnsolicitedUserData(channel <-chan readWriteModel.S7MessageUserData) {
for message := range channel {
c.subscriberMutex.Lock()