工业现场总线Fieldbus变量的统一管理容器设计

public class FieldbusVariableContainer
{
    private readonly Controller controller;

    private const string ElementCountTerminator = ".count";

    private PCModbusSlaveConnectionVariableContainer pcModbusSlaveVars;

    private INamedConstantCollection<PCModbusMasterConnectionVariableContainer, ModbusMasterConnectionInformation> pcModbusMasterVars;

    private INamedConstantCollection<DriveModbusMasterConnectionVariableContainer, DriveModbusMasterConnectionInformation> driveModbusMasterVars;

    private INamedConstantCollection<HilscherConnectionVariableContainer, HilscherConnectionInformation> hilscherVars;

    public Variable this[string name]
    {
        get
        {
            populateVariables(force: false);
            if (pcModbusSlaveVars != null)
            {
                Variable variable = pcModbusSlaveVars[name];
                if (variable != null)
                {
                    return variable;
                }
            }

            foreach (HilscherConnectionVariableContainer hilscherVar in hilscherVars)
            {
                Variable variable2 = hilscherVar[name];
                if (variable2 != null)
                {
                    return variable2;
                }
            }

            foreach (DriveModbusMasterConnectionVariableContainer driveModbusMasterVar in driveModbusMasterVars)
            {
                Variable variable3 = driveModbusMasterVar[name];
                if (variable3 != null)
                {
                    return variable3;
                }
            }

            foreach (PCModbusMasterConnectionVariableContainer pcModbusMasterVar in pcModbusMasterVars)
            {
                Variable variable4 = pcModbusMasterVar[name];
                if (variable4 != null)
                {
                    return variable4;
                }
            }

            return null;
        }
    }

    public PCModbusSlaveConnectionVariableContainer PCModbusSlave
    {
        get
        {
            populateVariables(force: false);
            return pcModbusSlaveVars;
        }
    }

    public INamedConstantCollection<PCModbusMasterConnectionVariableContainer, ModbusMasterConnectionInformation> PCModbusMaster
    {
        get
        {
            populateVariables(force: false);
            return pcModbusMasterVars;
        }
    }

    public INamedConstantCollection<DriveModbusMasterConnectionVariableContainer, DriveModbusMasterConnectionInformation> DriveModbusMaster
    {
        get
        {
            populateVariables(force: false);
            return driveModbusMasterVars;
        }
    }

    public INamedConstantCollection<HilscherConnectionVariableContainer, HilscherConnectionInformation> Hilscher
    {
        get
        {
            populateVariables(force: false);
            return hilscherVars;
        }
    }

    internal FieldbusVariableContainer(Controller c)
    {
        if (c == null)
        {
            throw new ArgumentNullException("c");
        }

        controller = c;
        controller.Information.ControllerResettingPre += delegate
        {
            invalidateCache();
        };
    }

    private void invalidateCache()
    {
        pcModbusSlaveVars = null;
        pcModbusMasterVars = null;
        driveModbusMasterVars = null;
        hilscherVars = null;
    }

    private void populateVariables(bool force)
    {
        if (force)
        {
            invalidateCache();
        }
        else if (hilscherVars != null && driveModbusMasterVars != null && pcModbusMasterVars != null)
        {
            return;
        }

        Dictionary<int, HilscherConnectionVariableContainer> dictionary = new Dictionary<int, HilscherConnectionVariableContainer>();
        Dictionary<int, DriveModbusMasterConnectionVariableContainer> dictionary2 = new Dictionary<int, DriveModbusMasterConnectionVariableContainer>();
        Dictionary<int, PCModbusMasterConnectionVariableContainer> dictionary3 = new Dictionary<int, PCModbusMasterConnectionVariableContainer>();
        Dictionary<int, PCModbusSlaveConnectionVariableContainer> dictionary4 = new Dictionary<int, PCModbusSlaveConnectionVariableContainer>();
        StringBuilder stringBuilder = new StringBuilder(260);
        Wrapper.UtilFieldbusGetDefinesFilePath(stringBuilder, stringBuilder.Capacity);
        string[] defineNamesFromFile = CoreVariableHelper.GetDefineNamesFromFile(controller, stringBuilder.ToString());
        IntPtr phCompiler_ = IntPtr.Zero;
        ExceptionResolver.ResolveThrow(Wrapper.AerCompilerOpen(ref phCompiler_));
        using (CompilerHandle compilerHandle = new CompilerHandle(phCompiler_))
        {
            string[] array = defineNamesFromFile;
            foreach (string name in array)
            {
                if (name.EndsWith(".count"))
                {
                    continue;
                }

                int result = 1;
                if (Array.Exists(defineNamesFromFile, (string defineName) => defineName == name + ".count"))
                {
                    StringBuilder stringBuilder2 = new StringBuilder(4096);
                    ExceptionResolver.ResolveThrow(controller, Wrapper.AerCompilerGetSubstitutionTextForDefine(compilerHandle.Value, name + ".count", stringBuilder2, stringBuilder2.Capacity));
                    int.TryParse(stringBuilder2.ToString().Trim(), out result);
                }

                List<Variable> list = new List<Variable>();
                if (result > 1)
                {
                    for (int j = 0; j < result; j++)
                    {
                        _PTR_DATA pVariableInfo_ = default(_PTR_DATA);
                        string text = $"{name}[{j}]";
                        ExceptionResolver.ResolveThrow(controller, Wrapper.AerCompilerParseVariableInfo(compilerHandle.Value, text, ref pVariableInfo_));
                        list.Add(CoreVariableHelper.CreateFieldbusVariable(controller, text, pVariableInfo_));
                    }
                }
                else
                {
                    _PTR_DATA pVariableInfo_2 = default(_PTR_DATA);
                    ExceptionResolver.ResolveThrow(controller, Wrapper.AerCompilerParseVariableInfo(compilerHandle.Value, name, ref pVariableInfo_2));
                    list.Add(CoreVariableHelper.CreateFieldbusVariable(controller, name, pVariableInfo_2));
                }

                Variable firstVar = list[0];
                if (list[0] == null)
                {
                    continue;
                }

                int num = (Array.Exists(CoreVariableHelper.GetHasConnectionContexts(), (VariableContext varContext) => varContext == firstVar.Context) ? firstVar.CoreIdentifier.arrayDimension[0].dwValue : 0);
                switch (firstVar.Context)
                {
                    case VariableContext.FieldbusInput:
                    case VariableContext.FieldbusOutput:
                        if (!dictionary.ContainsKey(num))
                        {
                            _HilscherConnectionInfo pConnInfo_3 = default(_HilscherConnectionInfo);
                            ExceptionResolver.ResolveThrow(controller, Wrapper.AerFieldbusGetHilscherConnectionInfo(controller.Handle.Value, (short)num, ref pConnInfo_3));
                            HilscherConnectionInformation connInfo3 = new HilscherConnectionInformation(pConnInfo_3.displayName, pConnInfo_3.id, (FieldbusType)pConnInfo_3.type);
                            dictionary[num] = new HilscherConnectionVariableContainer(connInfo3);
                        }

                        foreach (Variable item in list)
                        {
                            dictionary[num].AddVariable(item);
                        }

                        break;
                    case VariableContext.DriveModbusMasterInputWord:
                    case VariableContext.DriveModbusMasterOutputWord:
                    case VariableContext.DriveModbusMasterOutputWordStatus:
                    case VariableContext.DriveModbusMasterInputBit:
                    case VariableContext.DriveModbusMasterOutputBit:
                    case VariableContext.DriveModbusMasterOutputBitStatus:
                        if (!dictionary2.ContainsKey(num))
                        {
                            _DriveModbusMasterConnectionInfo pConnInfo_2 = default(_DriveModbusMasterConnectionInfo);
                            ExceptionResolver.ResolveThrow(controller, Wrapper.AerFieldbusGetDriveModbusMasterConnectionInfo(controller.Handle.Value, (short)num, ref pConnInfo_2));
                            DriveModbusMasterConnectionInformation connInfo2 = new DriveModbusMasterConnectionInformation(pConnInfo_2.displayName, pConnInfo_2.slaveId, IPAddress.Parse(pConnInfo_2.slaveIp), pConnInfo_2.slavePort, pConnInfo_2.axis);
                            dictionary2[num] = new DriveModbusMasterConnectionVariableContainer(connInfo2);
                        }

                        foreach (Variable item2 in list)
                        {
                            dictionary2[num].AddVariable(item2);
                        }

                        break;
                    case VariableContext.ModbusMasterInputWord:
                    case VariableContext.ModbusMasterOutputWord:
                    case VariableContext.ModbusMasterOutputWordStatus:
                    case VariableContext.ModbusMasterInputBit:
                    case VariableContext.ModbusMasterOutputBit:
                    case VariableContext.ModbusMasterOutputBitStatus:
                        if (!dictionary3.ContainsKey(num))
                        {
                            _PCModbusMasterConnectionInfo pConnInfo_4 = default(_PCModbusMasterConnectionInfo);
                            ExceptionResolver.ResolveThrow(controller, Wrapper.AerFieldbusGetPCModbusMasterConnectionInfo(controller.Handle.Value, (short)num, ref pConnInfo_4));
                            ModbusMasterConnectionInformation connInfo4 = new ModbusMasterConnectionInformation(pConnInfo_4.displayName, pConnInfo_4.slaveId, IPAddress.Parse(pConnInfo_4.slaveIp), pConnInfo_4.slavePort);
                            dictionary3[num] = new PCModbusMasterConnectionVariableContainer(connInfo4);
                        }

                        foreach (Variable item3 in list)
                        {
                            dictionary3[num].AddVariable(item3);
                        }

                        break;
                    case VariableContext.ModbusSlaveInputWord:
                    case VariableContext.ModbusSlaveOutputWord:
                    case VariableContext.ModbusSlaveInputBit:
                    case VariableContext.ModbusSlaveOutputBit:
                        if (!dictionary4.ContainsKey(num))
                        {
                            _PCModbusSlaveConnectionInfo pConnInfo_ = default(_PCModbusSlaveConnectionInfo);
                            ExceptionResolver.ResolveThrow(controller, Wrapper.AerFieldbusGetPCModbusSlaveConnectionInfo(controller.Handle.Value, ref pConnInfo_));
                            ModbusSlaveConnectionInformation connInfo = new ModbusSlaveConnectionInformation(pConnInfo_.displayName, pConnInfo_.id, pConnInfo_.inputWords, pConnInfo_.outputWords, pConnInfo_.inputBits, pConnInfo_.outputBits);
                            dictionary4[num] = new PCModbusSlaveConnectionVariableContainer(connInfo);
                        }

                        foreach (Variable item4 in list)
                        {
                            dictionary4[num].AddVariable(item4);
                        }

                        break;
                }
            }
        }

        driveModbusMasterVars = new NamedConstantCollection<DriveModbusMasterConnectionVariableContainer, DriveModbusMasterConnectionInformation>(dictValues2Array(dictionary2));
        pcModbusMasterVars = new NamedConstantCollection<PCModbusMasterConnectionVariableContainer, ModbusMasterConnectionInformation>(dictValues2Array(dictionary3));
        pcModbusSlaveVars = ((dictionary4.Count > 0) ? dictionary4[0] : null);
        hilscherVars = new NamedConstantCollection<HilscherConnectionVariableContainer, HilscherConnectionInformation>(dictValues2Array(dictionary));
    }

    private static TValue[] dictValues2Array<TKey, TValue>(IDictionary<TKey, TValue> dict)
    {
        return new List<TValue>(dict.Values).ToArray();
    }
}

以下是针对 FieldbusVariableContainer 类的专业解析,结合工业通信协议和软件设计模式的深度分析:


1. 核心功能定位

该类是 工业现场总线(Fieldbus)变量的统一管理容器,支持多种工业通信协议:

Modbus TCP Slave/Master(PC端)

Modbus TCP Master(驱动器端)

Hilscher协议(专有工业通信协议)

通过分层架构实现对不同设备通信变量的统一访问。


2. 架构设计

2.1 成员结构
成员 类型 协议类型 典型变量示例
pcModbusSlaveVars PCModbusSlaveConnectionVariableContainer Modbus TCP Slave "Slave1.InputWord[0]"
pcModbusMasterVars INamedConstantCollection<PCModbusMaster..., ModbusMasterConnInfo> Modbus TCP Master "Master1.OutputBit[2]"
driveModbusMasterVars INamedConstantCollection<DriveModbusMaster..., DriveModbusConnInfo> 驱动器Modbus Master "Drive1.InputWord[10]"
hilscherVars INamedConstantCollection<HilscherConnection..., HilscherConnInfo> Hilscher协议 "Hilscher1.Output[3]"
2.2 核心机制

延迟加载:通过 populateVariables(force: false) 实现按需初始化

缓存管理:控制器复位时自动清空缓存(ControllerResettingPre 事件绑定)

动态解析:从配置文件解析变量定义(defineNamesFromFile


3. 关键流程解析

3.1 变量初始化流程 (populateVariables)

 3.2 变量查找逻辑 (this[name])

// 搜索顺序优先级
1. Modbus Slave变量 
2. Hilscher协议变量
3. 驱动器Modbus变量
4. PC Modbus Master变量

4. 协议特定处理

4.1 Modbus TCP 变量
// 典型变量上下文
VariableContext.ModbusSlaveInputWord    // 从站输入寄存器
VariableContext.ModbusMasterOutputBit  // 主站输出线圈

// 连接信息解析
_PCModbusSlaveConnectionInfo slaveInfo;
Wrapper.AerFieldbusGetPCModbusSlaveConnectionInfo(..., ref slaveInfo);

特点

区分主/从站模式

支持线圈(Bit)和寄存器(Word)操作

4.2 Hilscher协议变量
_HilscherConnectionInfo hilscherInfo;
Wrapper.AerFieldbusGetHilscherConnectionInfo(..., ref hilscherInfo);

特点

使用专有连接ID标识设备

支持多种Fieldbus类型(通过FieldbusType枚举)


5. 工业级实现细节

5.1 变量维度处理
if (name.EndsWith(".count")) // 忽略维度定义文件
if (result > 1) // 处理数组型变量
{
    for (int j = 0; j < result; j++) {
        string text = $"{name}[{j}]"; // 构造带下标的变量名
        // 解析每个数组元素...
    }
}

作用:支持PLC中常见的数组变量(如 InputWord[0..15]

5.2 编译器集成
using (CompilerHandle compilerHandle = new CompilerHandle(phCompiler_)) 
{
    Wrapper.AerCompilerParseVariableInfo(..., name, ref pVariableInfo_);
}

必要性:依赖编译器解析变量地址映射关系。

5.3 连接信息缓存
Dictionary<int, DriveModbusMasterConnectionVariableContainer> dictionary2 = new ...;
if (!dictionary2.ContainsKey(num)) 
{
    // 首次访问时初始化连接信息
    dictionary2[num] = new DriveModbusMasterConnectionVariableContainer(...);
}

优化:避免重复查询硬件连接信息。


6. 典型使用场景

// 获取现场总线容器
var fieldbus = new FieldbusVariableContainer(controller);

// 访问Modbus Slave输入寄存器
double slaveInput = fieldbus["Slave1.InputWord[0]"].Value;

// 批量获取Hilscher变量
foreach (var hilscherVar in fieldbus.Hilscher) {
    // 处理每个Hilscher设备变量...
}

// 设置驱动器Modbus输出
fieldbus.DriveModbusMaster[0].OutputWord[1].Value = 100;

7. 设计亮点

协议抽象
统一接口访问不同现场总线协议,上层无需关心底层差异

动态扩展
通过 INamedConstantCollection 支持运行时设备热插拔

工业标准兼容
严格遵循Modbus和Hilscher协议规范

资源安全
using 语句确保编译器句柄释放


8. 改进建议

异步支持:增加 GetVariableAsync 方法适应高并发场景

变量监控:实现 IVariableObserver 接口监听变量变化

协议扩展:通过策略模式方便添加新协议(如PROFINET)

该设计完美体现了工业控制软件对 多协议兼容 和 实时性 的要求,是现场总线通信层的典范实现。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容