首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PLC逻辑库

PLC逻辑库
EN

Code Review用户
提问于 2019-07-22 14:29:47
回答 2查看 190关注 0票数 0

我一直在C++中实现一个可编程逻辑模块库(即和门、或门、RS触发器和TON、TOF和TP定时器)。我决定用实现公共接口的C++类对所有这些逻辑块进行建模。

代码语言:javascript
复制
namespace LogicBlocks
{

class LogicBlk {
public:
    enum LogicType_e{
        POS, // positive logic
        NEG  // negative logic
    };
    virtual void Update(void) = 0;

private:

};

}

我已经实现了定时器和RS触发器逻辑块。我现在要做的是实现AND和OR门。我的想法是使用和门和或门与1输入最多8个输入。我以下列方式执行了这一规定。每种类型的门我都有特殊的课程。例如:

具有两个输入的OR门

接口:

代码语言:javascript
复制
namespace LogicBlocks
{

// OR logic gate with two inputs 
class Or_02 : public LogicBlk{
public:
    Or_02(uint32_t *bitsArray, 
          uint32_t input_01, LogicType_e inputType_01, 
          uint32_t input_02, LogicType_e inputType_02, 
          uint32_t output);
    virtual ~Or_02();

    void Update(void);

private:

    uint32_t    m_In01;
    LogicType_e m_In01Type;
    uint32_t    m_In02;
    LogicType_e m_In02Type;
    uint32_t    m_Out;
    uint32_t    *m_BitsArray;

};

}

执行情况:

代码语言:javascript
复制
LogicBlocks::Or_02::Or_02(uint32_t *bitsArray, 
                          uint32_t input_01, LogicType_e inputType_01,
                          uint32_t input_02, LogicType_e inputType_02, 
                          uint32_t output):
        m_BitsArray{bitsArray}, 
        m_In01{input_01}, m_In01Type{inputType_01}, 
        m_In02{input_02}, m_In02Type{inputType_02}, 
        m_Out{output}{
}

LogicBlocks::Or_02::~Or_02() {
}

void LogicBlocks::Or_02::Update(void){

    if(((Utils::TestBitSet(m_BitsArray, m_In01) && m_In01Type == POS) || (Utils::TestBitClr(m_BitsArray, m_In01) && m_In01Type == NEG)) || 
       ((Utils::TestBitSet(m_BitsArray, m_In02) && m_In02Type == POS) || (Utils::TestBitClr(m_BitsArray, m_In02) && m_In02Type == NEG))){
        Utils::SetBit(m_BitsArray, m_Out);
    }else{
        Utils::ClrBit(m_BitsArray, m_Out);
    }
}

具有三个输入的<#>OR门

接口:

代码语言:javascript
复制
namespace LogicBlocks
{

// OR logic gate with three inputs
class Or_03 : public LogicBlk{
public:
    Or_03(uint32_t* const bitsArray, 
          const uint32_t input_01, const LogicType_e inputType_01, 
          const uint32_t input_02, const LogicType_e inputType_02, 
          const uint32_t input_03, const LogicType_e inputType_03, 
          const uint32_t output);
    virtual ~Or_03();

    void Update(void);

private:

    uint32_t    m_In01;
    LogicType_e m_In01Type;
    uint32_t    m_In02;
    LogicType_e m_In02Type;
    uint32_t    m_In03;
    LogicType_e m_In03Type;
    uint32_t    m_Out;
    uint32_t    *m_BitsArray;

};

} 

执行情况:

代码语言:javascript
复制
LogicBlocks::Or_03::Or_03(uint32_t* const bitsArray, 
                          const uint32_t input_01, const LogicType_e inputType_01,
                          const uint32_t input_02, const LogicType_e inputType_02, 
                          const uint32_t input_03, const LogicType_e inputType_03,
                          const uint32_t out):
        m_BitsArray{bitsArray}, 
        m_In01{input_01}, m_In01Type{inputType_01}, 
        m_In02{input_02}, m_In02Type{inputType_02}, 
        m_In03{input_03}, m_In03Type{inputType_03}, 
        m_Out{output}{
}

LogicBlocks::Or_03::~Or_03() {
}

void LogicBlocks::Or_03::Update(void){

    if(((Utils::TestBitSet(m_BitsArray, m_In01) && m_In01Type == POS) || (Utils::TestBitClr(m_BitsArray, m_In01) && m_In01Type == NEG)) || 
       ((Utils::TestBitSet(m_BitsArray, m_In02) && m_In02Type == POS) || (Utils::TestBitClr(m_BitsArray, m_In02) && m_In02Type == NEG)) ||
       ((Utils::TestBitSet(m_BitsArray, m_In03) && m_In03Type == POS) || (Utils::TestBitClr(m_BitsArray, m_In03) && m_In03Type == NEG))){
        Utils::SetBit(m_BitsArray, m_Out);
    }else{
        Utils::ClrBit(m_BitsArray, m_Out);
    }
}

这是用法。假设我有一个名为Logic的对象:

接口:

代码语言:javascript
复制
#include 
#include "LogicBlk.h"

namespace Logic
{

class Logic{
public:
    Logic();
    virtual ~Logic();

    // method shall be called from a task
    void Loop(void);

private:

    uint32_t m_BitsArray[1] = {0};

    static const uint8_t NO_LOGIC_BLKS = 2;
    LogicBlocks::LogicBlk* m_LogicBlks[NO_LOGIC_BLKS];

};

}

执行情况:

代码语言:javascript
复制
#include "Logic.h"
#include "Bits.h"
#include "Or_02.h"
#include "And_02.h"

    #define LW_01       (0)

    // Byte 01         
    #define LSig01      (LW_01*32 + 0x00)   
    #define LSig02      (LW_01*32 + 0x01)   
    #define LSig03      (LW_01*32 + 0x02)
    //efine L           (LW_01*32 + 0x03)
    //efine L           (LW_01*32 + 0x04)
    //efine L           (LW_01*32 + 0x05)
    //efine L           (LW_01*32 + 0x06)
    //efine L           (LW_01*32 + 0x07)

    // Byte 02
    #define LAx01       (LW_01*32 + 0x08)
    #define LAx02       (LW_01*32 + 0x09)
    //efine L           (LW_01*32 + 0x0A)
    //efine L           (LW_01*32 + 0x0B)
    //efine L           (LW_01*32 + 0x0C)
    //efine L           (LW_01*32 + 0x0D)
    //efine L           (LW_01*32 + 0x0E)
    //efine L           (LW_01*32 + 0x0F)

    // Byte 03
    //efine L           (LW_01*32 + 0x10)
    //efine L           (LW_01*32 + 0x11)
    //efine L           (LW_01*32 + 0x12)
    //efine L           (LW_01*32 + 0x13)
    //efine L           (LW_01*32 + 0x14)
    //efine L           (LW_01*32 + 0x15)
    //efine L           (LW_01*32 + 0x16)
    //efine L           (LW_01*32 + 0x17)

    // Byte 04
    //efine L           (LW_01*32 + 0x18)
    //efine L           (LW_01*32 + 0x19)
    //efine L           (LW_01*32 + 0x1A)
    //efine L           (LW_01*32 + 0x1B)
    //efine L           (LW_01*32 + 0x1C)
    //efine L           (LW_01*32 + 0x1D)
    //efine L           (LW_01*32 + 0x1E)
    //efine L           (LW_01*32 + 0x1F)

Logic::Logic::Logic(){

    m_LogicBlks[0] = new LogicBlocks::Or_02(m_BitsArray,
                                            LSig01,       LogicBlocks::LogicBlk::POS,
                                            LSig02,       LogicBlocks::LogicBlk::POS,
                                            LAx01);
    m_LogicBlks[1] = new LogicBlocks::And_02(m_BitsArray,
                                             LAx01,      LogicBlocks::LogicBlk::POS,
                                             LSig03,     LogicBlocks::LogicBlk::NEG,
                                             LAx02);

}

Logic::Logic::~Logic(){
}

void Logic::Logic::Loop(void){

    for(uint8_t curBlk = 0; curBlk < NO_LOGIC_BLKS; curBlk++){
        m_LogicBlks[curBlk]->Update();
    }

}

为了完整性,实用程序函数用于处理单个位。

代码语言:javascript
复制
bool Utils::TestBitSet(uint32_t *bitsArray, uint32_t bit){
    uint32_t wordValue    = *(bitsArray + (bit >> 5));
    uint32_t bitPosInWord = (bit - ((bit >> 5) << 5));

    return ((wordValue & ((uint32_t)1 << bitPosInWord)) >> bitPosInWord) ? true : false;
}

bool Utils::TestBitClr(uint32_t *bitsArray, uint32_t bit){
    uint32_t wordValue    = *(bitsArray + (bit >> 5));
    uint32_t bitPosInWord = (bit - ((bit >> 5) << 5));

    return ((wordValue & ((uint32_t)1 << bitPosInWord)) >> bitPosInWord) ? false : true; 
}

void Utils::SetBit(uint32_t *bitsArray, uint32_t bit){
    uint32_t word         = (bit >> 5);
    uint32_t bitPosInWord = (bit - ((bit >> 5) << 5));

    *(bitsArray + word) |= ((uint32_t)1 << bitPosInWord);
}

void Utils::ClrBit(uint32_t *bitsArray, uint32_t bit){
    uint32_t word         = (bit >> 5);
    uint32_t bitPosInWord = (bit - ((bit >> 5) << 5));

    *(bitsArray + word) &= ~((uint32_t)1 << bitPosInWord);
}

void Utils::NegBit(uint32_t *bitsArray, uint32_t bit){
    if(TestBitSet(bitsArray, bit)){
        ClrBit(bitsArray, bit);
    }else{
        SetBit(bitsArray, bit);
    }
}

现在,我一直在思考这一实施的弱点。首先,有三个输入的OR门的实现是对具有两个输入的OR门的某种代码重复。更多输入的OR门也是如此。第二个问题是门是如何“沟通”和形成整个逻辑的。这里我一直在使用位数组。我还考虑到了每个逻辑块接收到协作逻辑块的指针的可能性。我之所以不使用这种方法,是因为我认为这种实现会导致树形数据结构,在对整个逻辑进行评估的情况下,堆栈可以快速增长,特别是在复杂的逻辑结构中。

有没有人知道如何解决我的问题,即代码重复和逻辑块之间的通信?谢谢你的建议。

EN

回答 2

Code Review用户

发布于 2019-07-22 17:47:30

参数化逻辑类型

首先,我尝试将从电压级别到逻辑级别的映射合并到一个地方。例如:

代码语言:javascript
复制
class positive_signal { 
    unsigned value : 1;
public:
     bool isSet() const { return value == 1; }
     void set() { value = 1; }
     void clear() { value = 0; }
};

class negative_signal {
    unsigned value : 1;
public:
    bool isSet() const { return value == 0; }
    void set() { value = 0; }
    void clear() { value = 1; }
};

请注意,我在这里遗漏了很多细节,只是给出了一个总体方向的草图。例如,在实践中,您很有可能希望setclear成员函数能够支持return *this;

推广盖茨

我会考虑实现每种门类型,以获得任意数量的输入:

代码语言:javascript
复制
namespace logic {

    bool OR(std::vector const &inputs) { 
        return std::any_of(inputs().begin(), inputs.end(),
                           [](signal in) { return in.isSet; });
    }

    bool AND(std::vector const &inputs) { 
        return std::all_of(inputs.begin(), inputs.end(), 
                           [](signal in) { return in.isSet; });
    }
    // and so on
}

这避免了具有不同输入数的门的重复逻辑,因为对于这两种输入都使用完全相同的代码。

集成

不过,把这两者结合起来可能有点棘手。正如我上面展示的代码,它们不太适合。你可以走几条不同的路线。一种是使用继承,因此您可以从signal作为一个抽象基类开始,然后从它派生出正负信号。如果这样做,就必须将指针的向量传递给信号,而不是信号的向量。

或者,您可以将信号类型作为模板参数传递给门,这样编译器将实例化一个负逻辑的OR,为正逻辑实例化一个OR (以此类推)。

在这种情况下,我认为后者更合适。如果您期望创建一个类似于5输入或门的东西,其中2个任意输入是负逻辑,而另三个正逻辑(或者更一般地说,输入可以是任何正负逻辑的任意组合),继承就会有意义。然而,在现实中,您通常在门级定义逻辑类型,因此一个5输入OR门将把所有5个输入作为正逻辑,或者将所有5个输入作为负逻辑,但绝不将两者结合在一起。

票数 4
EN

Code Review用户

发布于 2019-07-22 16:57:05

代码语言:javascript
复制
LogicBlocks::Or_02::Or_02(uint32_t *bitsArray, 
                          uint32_t input_01, LogicType_e inputType_01,
                          uint32_t input_02, LogicType_e inputType_02, 
                          uint32_t output):
        m_BitsArray{bitsArray}, 
        m_In01{input_01}, m_In01Type{inputType_01}, 
        m_In02{input_02}, m_In02Type{inputType_02}, 
        m_Out{output}{
}

成员变量是按照类中指定的顺序初始化的,而不是初始化程序列表中的顺序(因此m_BitsArray将被最后初始化)。虽然这里没有什么会中断,但最好始终在初始化程序列表中使用正确的顺序。

代码语言:javascript
复制
(Utils::TestBitSet(m_BitsArray, m_In01) && m_In01Type == POS)

有很多这样的代码。为什么不将其考虑到一个函数中,同时考虑所有三个变量:

代码语言:javascript
复制
Utils::TestBitSet(m_BitsArray, m_In01, m_In01Type);

我不太喜欢分开的TestBitSet()TestBitClr()。只要有一个IsBitSet()并在适当的地方使用!IsBitSet()就更整洁了。

代码语言:javascript
复制
new LogicBlocks::Or_02

不要只漏记忆。使用std::unique_ptr,或者手动删除它。

LogicBlk必须有一个虚拟析构函数(然后也不需要在派生类中指定空析构函数)。

virtual Update(void);

void参数在C++中是不必要的。

代码语言:javascript
复制
#define LSig01      (LW_01*32 + 0x00)
...

#defines相比,更喜欢常量静态变量,因为它们有适当的作用范围。

代码语言:javascript
复制
Or_03(uint32_t* const bitsArray, 
      const uint32_t input_01, const LogicType_e inputType_01, ...

不要制作由值const传递的函数参数。这些consts对打电话的人来说并不重要,他们隐藏了那些确实重要的consts (例如& vs const&* vs const*),这使得声明很难读懂。C++还允许在声明中使用const与函数定义中使用const不同,因此甚至可能会产生误导。

我们可以对不同数量的输入使用模板:

代码语言:javascript
复制
struct LogicInput {
    LogicType_e type;
    std::uint32_t value;
};

template
class Or {
public:

    Or(uint32_t* bitsArray, std::array, NumInputs> in, uint32_t out);

    ...

    std::array m_In;
    std::uint32_t m_Out;
    uint32_t* m_BitsArray;
};
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/224676

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档