Как обрабатывать множество тем MQTT по подписке в TwinCAT, используя Tc3_IoTBase - PullRequest
0 голосов
/ 17 апреля 2019

Я могу прочитать несколько тем подписки, используя этот код.Тем не менее, я подписан на множество разных тем, и длинное выражение if замедляет мой код.Мне уже пришлось довести количество тактов до 20 (это, вероятно, излишне, но 10 было недостаточно) в PlcTask.Я ищу более разумное решение, которое будет работать с меньшим количеством циклов.В приведенном ниже коде ясно, как долго длится этот оператор IF, и это даже не весь оператор if (только для тем machine и 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 / ускорение '' motion2 / ускорение 'и т. д. и т. д. (надеюсь, вы поняли идею).Так что я уже был в состоянии подписаться на все темы motion1, подписавшись на 'motion1 / #'.Поэтому я попытался использовать fb.Message.CompareTopic (sTopic: = 'motion1 / #'), чтобы найти темы, относящиеся к motion1, а затем оператор if, который распознает темы 'motion1 / somethingsomething'.Однако 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, так что это действительно приятно.Однако должен быть более эффективный способ сделать это.Я попробовал метод, показанный во втором коде, но это не сработало.

Ответы [ 2 ]

0 голосов
/ 18 апреля 2019

Хорошо, есть много вещей, которые можно улучшить.Сначала давайте создадим 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 означает Сергей Романов, вы можете использовать любой префикс

Теперь давайте создадим вам массив движенийиметь.Допустим, у вас есть 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, вы можете использовать эту функцию, которая будет извлекать последний символ и преобразовываться в 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 голосов
/ 18 апреля 2019

Я решил свою проблему!Вот мой код:

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
...