0

I'm able to read multiple subscription topics by using this code. However I'm subscribed to a lot of different topics and the long if statement is slowing down my code. I already had to up the amount of cycle ticks to 20 (this is probably overkill, but 10 was not enough) in PlcTask. I'm looking for a smarter solution that will function with less cycle ticks. In the code shown below it is clear how long this IF statement is getting and this is not even the entire if statement (just for the topics machine and 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

My topics are build up like 'motion1/position' 'motion1/acceleration' 'motion2/acceleration' etc etc (I hope you get the idea). So I was already able to subscribe to all motion1 topics by subscribing to 'motion1/#'. So I tried to use fb.Message.CompareTopic(sTopic:='motion1/#') to find the topics belonging to motion1 and than an if statement that recognizes the topics 'motion1/somethingsomething'. However the fb.Message.CompareTopic(sTopic:='motion1/#') did not recognize motion1 topics.

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

So the first code that I showed does read all of the messages that I need from MQTT so that is really nice. However there should be a more efficient way to do this. I tried the method shown in the second code, however that did not work.

Louise
  • 3
  • 2
  • Please do not edit questions to include solutions (and do NOT add "Solved" to the title). Add an answer using the box under the question and then mark it as accepted as this helps others find the answer. – hardillb Apr 17 '19 at 14:41

2 Answers2

0

I solved my problem! Here is my code:

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
Louise
  • 3
  • 2
  • You've solved it but your code is still bulky and a lot of clutter, see my example how to make your code more programatic and slick. – Sergey Romanov Apr 18 '19 at 10:41
0

Ok, there are a lot of things to improve. First lets create SPLIT function that will convert topic to array

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

Now how you can find if current topic is 'motion1' related

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

But since you then load different payloads to different variables I would create a map. I do nto know all your program, but from what I see I would start with:

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

SR means Sergey Romanov, you can use any prefix

Now lets create array of motions you have. Let's say you have 3. This way we decouple motion number into separate variable. Note for your code to work smoothely without making to much junk code, your topics have to be not motion1/velocity but 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

Here is all your code for all 3 motions.

Edit: Add number excraction from topic

If you do not want to change topics to motion/1/velosity then you can use this function that will extract last char and convert to 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

And then in the code instead of i := STRING_TO_INT(arsTopic[1]); you can i := TOPIC_TO_INT(arsTopic[0]) and the rest of the code slightly change.

So if you use TOPIC_TO_INT(STRING#'message2') this function will return INT#2

Sergey Romanov
  • 2,949
  • 4
  • 23
  • 38