如何使用Tc3_IoTBase处理TwinCAT中的许多MQTT订阅主题

问题描述 投票:0回答:2

我可以使用this代码阅读多个订阅主题。但是我订阅了很多不同的主题,而if if语句正在减慢我的代码。我已经不得不在PlcTask中将循环滴答的数量增加到20(这可能是矫枉过正,但是10还不够)。我正在寻找一种更智能的解决方案,能够以更少的周期滴答作用。在下面显示的代码中,很明显这个IF语句有多长,这甚至不是整个if语句(仅适用于主题机器和motion1)。

IF fbMessageQueue.nQueuedMessages > 0 THEN
    IF fbMessageQueue.Dequeue(fbMessage:=fbMessage) THEN
        IF fbMessage.CompareTopic(sTopic:='machine/on') THEN
            fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
            Machine.bOnPB := STRING_TO_BOOL(sPayloadRcv);
        ELSIF fbMessage.CompareTopic(sTopic:='machine/off') THEN
            fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
            Machine.bOffPB := STRING_TO_BOOL(sPayloadRcv);
        ELSIF fbMessage.CompareTopic(sTopic:='motion1/position') THEN
            fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
            Motion.nMotion1Postion := STRING_TO_LREAL(sPayloadRcv);
        ELSIF fbMessage.CompareTopic(sTopic:='motion1/velocity') THEN
            fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
            Motion.nMotion1Velocity := STRING_TO_LREAL(sPayloadRcv);
        ELSIF fbMessage.CompareTopic(sTopic:='motion1/acceleration') THEN
            fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
            Motion.nMotion1Acceleration := STRING_TO_LREAL(sPayloadRcv);
        ELSIF fbMessage.CompareTopic(sTopic:='motion1/deceleration') THEN
            fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
            Motion.nMotion1Deceleration := STRING_TO_LREAL(sPayloadRcv);
        ELSIF fbMessage.CompareTopic(sTopic:='motion1/execute') THEN
            fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
            Motion.nMotion1Execute := STRING_TO_LREAL(sPayloadRcv);
        // same for motion2 and motion3
        END_IF
    END_IF
END_IF

我的主题建立起来像'motion1 / position''motion1 / acceleration''motion2 / acceleration'等等(我希望你能得到这个想法)。所以我已经能够通过订阅'motion1 /#'来订阅所有的motion1主题。所以我尝试使用fb.Message.CompareTopic(sTopic:='motion1 /#')来查找属于motion1的主题,而不是识别主题'motion1 / somethingsomething'的if语句。然而,fb.Message.CompareTopic(sTopic:='motion1 /#')无法识别motion1主题。

IF fbMessageQueue.nQueuedMessages > 0 THEN
    IF fbMessageQueue.Dequeue(fbMessage:=fbMessage) THEN
        IF fbMessage.CompareTopic(sTopic:='machine/on') THEN
            fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
            Machine.bOnPB := STRING_TO_BOOL(sPayloadRcv);
        ELSIF fbMessage.CompareTopic(sTopic:='machine/off') THEN
            fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
            Machine.bOffPB := STRING_TO_BOOL(sPayloadRcv);
        ELSIF fbMessage.CompareTopic(sTopic:='motion1/#') THEN
            IF fbMessage.CompareTopic(sTopic:='motion1/position') THEN
                fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
                Motion.nMotion1Postion := STRING_TO_LREAL(sPayloadRcv);
            ELSIF fbMessage.CompareTopic(sTopic:='motion1/velocity') THEN
                fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
                Motion.nMotion1Velocity := STRING_TO_LREAL(sPayloadRcv);
            ELSIF fbMessage.CompareTopic(sTopic:='motion1/acceleration') THEN
                fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
                Motion.nMotion1Acceleration := STRING_TO_LREAL(sPayloadRcv);
            ELSIF fbMessage.CompareTopic(sTopic:='motion1/deceleration') THEN
                fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
                Motion.nMotion1Deceleration := STRING_TO_LREAL(sPayloadRcv);
            ELSIF fbMessage.CompareTopic(sTopic:='motion1/execute') THEN
                fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
                Motion.nMotion1Execute := STRING_TO_LREAL(sPayloadRcv);
            END_IF
        // same for motion2 and motion3
        END_IF
    END_IF
END_IF

所以我展示的第一个代码确实从MQTT读取了我需要的所有消息,所以这非常好。但是应该有一种更有效的方法来做到这一点。我尝试了第二个代码中显示的方法,但是没有用。

mqtt plc twincat structured-text
2个回答
0
投票

好的,有很多事情需要改进。首先让我们创建将主题转换为数组的SPLIT函数

FUNCTION SPLIT : ARRAY[0..255] OF STRING(250)
VAR_INPUT
    STR: STRING(250);
    CHAR: STRING(1);
END_VAR
VAR
    iPos: INT;
    sTest: STRING(250);
    iIndex: INT;
    xFinish: BOOL;
END_VAR

    sTest := STR;
    REPEAT
        iPos := FIND(sTest, CHAR);

        IF iPos = 0 THEN
            SPLIT[iIndex] := sTest;
            xFinish := TRUE;
        ELSE
            SPLIT[iIndex] := LEFT(sTest, iPos - 1);
            sTest := RIGHT(sTest, LEN(sTest) - iPos);
        END_IF;
        iIndex := iIndex + 1;
    UNTIL (xFinish = TRUE)
    END_REPEAT;

END_FUNCTION

现在你如何找到当前主题是否与'motion1'相关

VAR
    arsTopic: ARRAY[0..255] OF STRING(250);
END_VAR

IF fbMessageQueue.nQueuedMessages > 0 THEN
    IF fbMessageQueue.Dequeue(fbMessage:=fbMessage) THEN
        fbMessage.GetTopic(pTopic:=ADR(sTopicRcv), nTopicSize := SIZEOF(sTopicRcv));
        arsTopic := SPLIT(sTopicRcv, '/');
        IF (arsTopic[0] = 'motion1') THEN
            // do your staff
        END_IF
    END_IF
END_IF

但是,既然你将不同的有效负载加载到不同的变量,我会创建一个映射。我不知道你所有的程序,但从我看到的我会开始:

TYPE SR_MOTION:
    STRUCT
        lrPostion: LREAL; 
        lrVelocity: LREAL; 
        lrAcceleration: LREAL; 
        lrDeceleration: LREAL; 
        lrExecute: LREAL; 
    END_STRUCT
END_TYPE

SR表示Sergey Romanov,您可以使用任何前缀

现在让我们创建一系列动作。假设你有3个。这样我们就可以将运动数分解为单独的变量。请注意,您的代码可以顺畅地工作而不需要太多的垃圾代码,您的主题必须不是motion1/velocity而是motion/1/velosity

VAR
    astMotions: ARRAY[1..3] OF SR_MOTION;
    arsTopic: ARRAY[0..255] OF STRING(250);
    i: INT;
END_VAR

IF fbMessageQueue.nQueuedMessages > 0 THEN
    IF fbMessageQueue.Dequeue(fbMessage:=fbMessage) THEN
        fbMessage.GetTopic(pTopic:=ADR(sTopicRcv), nTopicSize := SIZEOF(sTopicRcv));
        fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);

        arsTopic := SPLIT(sTopicRcv, '/');
        IF arsTopic[0] = 'machine' THEN
            IF arsTopic[1] = 'on' THEN
                Machine.bOnPB := STRING_TO_BOOL(sPayloadRcv);
            ELSIF arsTopic[1] = 'off' THEN
                Machine.bOffPB := STRING_TO_BOOL(sPayloadRcv);
            END_IF
        ELSIF arsTopic[0] = 'motion' THEN
            i := STRING_TO_INT(arsTopic[1]); // arsTopic[1] has motion number in `motion/1/velosity`
            IF arsTopic[2] = 'position' THEN
                astMotions[i].lrPosition := STRING_TO_LREAL(sPayloadRcv);
            ELSIF arsTopic[2] = 'velosity' THEN
                astMotions[i].lrVelosity := STRING_TO_LREAL(sPayloadRcv);
            ELSIF arsTopic[2] = 'acceleration' THEN
                astMotions[i].lrAcceleration := STRING_TO_LREAL(sPayloadRcv);
            ELSIF arsTopic[2] = 'Deceleration' THEN
                astMotions[i].lrDeceleration := STRING_TO_LREAL(sPayloadRcv);
            ELSIF arsTopic[2] = 'execute' THEN
                astMotions[i].lrExecute := STRING_TO_LREAL(sPayloadRcv);
            END_IF
        END_IF
    END_IF
END_IF

以下是所有3个动作的所有代码。

编辑:添加主题中的数字删除

如果您不想将主题更改为motion/1/velosity,那么您可以使用此函数来提取最后一个char并转换为int

FUNCTION TOPIC_TO_INT: INT
VAR_INPUT
    str: STRING;
END_VAR
VAR
    ps: POINTER TO ARRAY[0..200] OF BYTE;
END_VAR
    ps := ADR(str);
    TOPIC_TO_INT := BYTE_TO_INT(ps^[LEN(str) - 1]) - 48;
END_FUNCTION

然后在代码而不是i := STRING_TO_INT(arsTopic[1]);你可以i := TOPIC_TO_INT(arsTopic[0])和其余的代码略有变化。

所以如果你使用TOPIC_TO_INT(STRING#'message2')这个函数将返回INT#2


0
投票

我解决了我的问题!这是我的代码:

IF fbMessageQueue.nQueuedMessages > 0 THEN
    IF fbMessageQueue.Dequeue(fbMessage:=fbMessage) THEN
        fbMessage.GetTopic(pTopic:=ADR(sTopicRcv), nTopicSize := SIZEOF(sTopicRcv));
        IF fbMessage.CompareTopic(sTopic:='machine/on') THEN
            fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
            Machine.bOnPB := STRING_TO_BOOL(sPayloadRcv);
        ELSIF fbMessage.CompareTopic(sTopic:='machine/off') THEN
            fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
            Machine.bOffPB := STRING_TO_BOOL(sPayloadRcv);
        ELSIF INT_TO_BOOL(FIND(sTopicRcv,'motion1')) THEN
            IF fbMessage.CompareTopic(sTopic:='motion1/position') THEN
                fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
                Motion.nMotion1Postion := STRING_TO_LREAL(sPayloadRcv);
            ELSIF fbMessage.CompareTopic(sTopic:='motion1/velocity') THEN
                fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
                Motion.nMotion1Velocity := STRING_TO_LREAL(sPayloadRcv);
            ELSIF fbMessage.CompareTopic(sTopic:='motion1/acceleration') THEN
                fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
                Motion.nMotion1Acceleration := STRING_TO_LREAL(sPayloadRcv);
            ELSIF fbMessage.CompareTopic(sTopic:='motion1/deceleration') THEN
                fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
                Motion.nMotion1Deceleration := STRING_TO_LREAL(sPayloadRcv);
            ELSIF fbMessage.CompareTopic(sTopic:='motion1/execute') THEN
                fbMessage.GetPayload(pPayload:=ADR(sPayloadRcv), nPayloadSize:=SIZEOF(sPayloadRcv), bSetNullTermination:=TRUE);
                Motion.nMotion1Execute := STRING_TO_LREAL(sPayloadRcv);
            END_IF
        // same for motion2 and motion3
        END_IF
    END_IF
END_IF
© www.soinside.com 2019 - 2024. All rights reserved.