C#读取本地或远程OPC,DCS不用配置DCOM数据然后通过socket转发给其他电脑,或者通过MODBUS转发给PLC来读取,也可以通过UDP转发给200smart来自动获取数据(效率更高)。

最近在搞工控数据采集的时候发现个头疼的问题——DCOM配置实在太反人类!尤其是跨网络传OPC数据,防火墙、权限、安全策略折腾一圈下来,黄花菜都凉了。今天咱们直接绕过这个深坑,上代码实战几种免配置的骚操作。

先来整个Socket方案热热身。上服务端代码:

// 数据打包发送
byte[] BufferSerialize(object data)
{
    using (var ms = new MemoryStream())
    {
        new BinaryFormatter().Serialize(ms, data);
        return ms.ToArray();
    }
}

// 定时采集任务
void OpcDaemon()
{
    var opcItems = new Dictionary<string, object> 
    {
        {"PLC1.Temperature", ReadOpcValue("Channel1.Device1.Tag1")},
        {"PLC1.Pressure", ReadOpcValue("Channel1.Device1.Tag2")}
    };
    
    using (var client = new TcpClient("192.168.1.100", 502))
    using (var stream = client.GetStream())
    {
        byte[] buffer = BufferSerialize(opcItems);
        stream.Write(buffer, 0, buffer.Length);
    }
}

这里用了二进制序列化偷懒,实际生产环境建议用Protobuf或者MessagePack。重点在于不用开DCOM白名单,防火墙放行502端口完事,客户端那边随便用什么语言都能接。

要是对面接的是老古董PLC,得上Modbus协议转换。NuGet装个NModbus:

// ModbusTCP转发
void ModbusForward()
{
    var factory = new ModbusFactory();
    using (var client = factory.CreateMasterTcp("192.168.1.200"))
    {
        // 把OPC温度值映射到保持寄存器40001
        ushort[] registers = new ushort[] { (ushort)OpcData["Temperature"] };
        client.WriteMultipleRegisters(0, 0, registers);
    }
}

注意Modbus的寄存器地址要减1,写40001对应偏移量0。这种玩法适合需要协议兼容的场景,但频繁写入的话记得加个数据变化阈值,别把PLC写崩了。

说到高效传输,200smart这种设备直接走UDP才是王道:

// UDP广播模式
void UdpBroadcast()
{
    using (var client = new UdpClient())
    {
        client.EnableBroadcast = true;
        byte[] datagram = Encoding.ASCII.GetBytes($"TEMPR:{OpcData["Temperature"]};PRESS:{OpcData["Pressure"]}");
        client.Send(datagram, datagram.Length, new IPEndPoint(IPAddress.Broadcast, 11000));
    }
}

实测千兆网络下这种裸数据包能跑到5ms级延迟,比TCP三次握手快不是一星半点。不过记得做好校验机制,丢了包得能重发。接收端用个异步监听:

// 200smart侧接收
void UdpListener()
{
    var client = new UdpClient(11000);
    while (true)
    {
        var remoteEP = new IPEndPoint(IPAddress.Any, 0);
        byte[] data = client.Receive(ref remoteEP);
        ParseData(Encoding.ASCII.GetString(data));
    }
}

关键点在于数据格式约定,建议用分号分隔的键值对,方便解析。如果数据量大的话可以上二进制结构体,不过ASCII调试起来肉眼可见更友好。

这几种方案都实战验证过,核心思路就是让DCOM见鬼去吧!代码里没写异常处理是怕影响阅读,实际用的时候记得加上重连机制和心跳包。有更好玩的招数欢迎来杠~

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐