• S7netplus v0.9.0中IsAvailable属性的改进方法


    在对PLC在线检测的时候,可能会用到IsAvailable属性,但是这个属性在S7netplus v0.9.0版本中并不能让人放心使用。

    1、问题所在

    通过查看S7netplus v0.9.0的源码,可以得知在IsAvailable属性中调用了Connect方法,这就相当于重新建立了连接,使得原来的连接失效。在代码中含有“TODO: Fix This”的注释,可见源码也认为这样写代码不妥。

    /// <summary>
    /// Returns true if a connection to the PLC can be established
    /// </summary>
    public bool IsAvailable
    {
        //TODO: Fix This
        get
        {
            try
            {
                OpenAsync().GetAwaiter().GetResult();
                return true;
            }
            catch
            {
                return false;
            }
        }
    }

    2、问题解决

    在旧版本(S7netplus v0.1.9 2018/5/14)的源码中,是通过Socket连接来验证PLC是否可用的,如下所示。

    /// <summary>
    /// Returns true if a connection to the plc can be established
    /// </summary>
    public bool IsAvailable
    {
        get
        {
    
    #if NETFX_CORE
            return (!string.IsNullOrWhiteSpace(IP));
    #else
            using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
            {
                return Connect(socket) == ErrorCode.NoError;
            }
    #endif
        }
    }

    根据这个版本的IsAvailable属性的实现代码,可以在S7netplus v0.9.0的基础上自己封装相同作用的IsAvailable属性代码,以解决当前版本的问题。

    /// <summary>
    /// PLC连接是否可用
    /// <remarks>PLC初始化以后才有连接意义,返回ping结果</remarks>
    /// <remarks>该版本中Plc的IsAvailable属性等价于Open,因此不能在连接后使用该属性</remarks>
    /// </summary>
    public bool IsAvailable
    {
        get
        {
            if (_plc == null || string.IsNullOrWhiteSpace(_plc.IP))
            {
                return false;
            }
    
            using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
            {
                string errorMessage;
                var ret = Connect(socket, _plc.IP, _plc.Port, out errorMessage) == ErrorCode.NoError;
                if (!ret)
                {
                    LastErrorMsg = errorMessage;
                }
    
                return ret;
            }
        }
    }
    
    /// <summary>
    /// 连接Socket
    /// </summary>
    /// <param name="socket">socket对象</param>
    /// <param name="ip">IP地址</param>
    /// <param name="port">端口</param>
    /// <param name="errorMessage">错误信息</param>
    /// <returns>连接状态码</returns>
    private static ErrorCode Connect(Socket socket, string ip, int port, out string errorMessage)
    {
        var errorCode = ErrorCode.NoError;
        errorMessage = string.Empty;
    
        try
        {
            var server = new IPEndPoint(IPAddress.Parse(ip), port);
            socket.Connect(server);
            return errorCode;
        }
        catch (SocketException sex)
        {
            // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx
            errorCode = sex.SocketErrorCode == SocketError.TimedOut
                ? ErrorCode.IPAddressNotAvailable
                : ErrorCode.ConnectionError;
            errorMessage = sex.Message;
        }
        catch (Exception ex)
        {
            errorCode = ErrorCode.ConnectionError;
            errorMessage = ex.Message;
        }
    
        return errorCode;
    }

    3、问题延申

    在上面实现的IsAvailable属性代码中,使用Socket连接的方法固然能够实现PLC的连接测试,但是要想控制连接超时时间就不容易了,这里主要解决超时时间的问题。

    C#中有Ping对象,可以直接通过IP来和目标机器进行连接,也能够设置超时时间,因此是个不错的选择。需要注意的是,这个方法并不能判断指定端口是否打开,因此只能在PLC建立连接之后使用。

    /// <summary>
    /// 使用ping判断PLC是否在线
    /// <remarks>这个方法不能测试端口是否开放</remarks>
    /// </summary>
    public bool IsPlcOnline
    {
        get
        {
            if (_plc == null || string.IsNullOrWhiteSpace(_plc.IP))
            {
                return false;
            }
    
            return PingPlc(_plc.IP);
        }
    }
    
    /// <summary>
    /// 对PLC进行ping操作
    /// </summary>
    /// <param name="ip">IP地址</param>
    /// <returns>是否可以ping通</returns>
    private static bool PingPlc(string ip)
    {
        var ping = new Ping();
        var reply = ping.Send(ip, 1000);
        return reply != null && reply.Status == IPStatus.Success;
    }
  • 相关阅读:
    ACdream 1069 无耻的出题人
    ACdream 1064 完美数
    ACdream 1028 Path
    ACdream 1020 The Game about KILL
    ACdream 1015 Double Kings
    CodeForces
    Codeforces 390A( 模拟题)
    Codeforces 389B(十字模拟)
    Codeforces 389A (最大公约数)
    Codeforces 417 C
  • 原文地址:https://www.cnblogs.com/xhubobo/p/14581445.html
Copyright © 2020-2023  润新知