Author Topic: Creating a FIFO  (Read 19464 times)

Lorne Van Dusen

  • Jr. Member
  • Posts: 93
  • I'm a old guy
    • View Profile
Creating a FIFO
« on: February 14, 2017, 01:19:06 PM »
I have a message that is stored in A$ that I need to put into a FIFO
 I only want to put the Message into the FIFO each time Relay 46 turns on.
I am hoping that the message in A$ can be moved into a DM area starting from DM[1700] or can a Text Message only be stored in a Variable such as A$?
I have a sample of how a FIFO should be done but I am still a little confused, Especially with the DM[1]
In the Define Table FIFO_Size = 15
 IF DM[21] <> DM[22]
DM[22] =DM[21]
 // Start FIFO
FOR I = 1 to 16 // Shift old data by towards end of FIFO
  LSHIFT DM[1], FIFO_Size
NEXT
DM[1] = DM[21] // Add new word to beginning of FIFO
ENDIF

In My program the Message is stored in DM[1600]
The start of the FIFO is DM[1700] so I think that DM[21] in the sample would be my DM[1700] & the DM[22] in the sample program would be my DM[1701] does that mean I need to substitute DM[1] with my DM[1600] ?
In my actual program DM32[1] is where the HSCV is stored.


garysdickinson

  • Hero Member
  • Posts: 502
  • Old PLC Coder
    • View Profile
Re:Creating a FIFO
« Reply #1 on: February 14, 2017, 04:25:17 PM »
Lorne,

I need to ask some questions about your usage of a FIFO. I assume that you are aware that a FIFO is some sort of data structure used to store and retrieve strings on a "First In, First Out" basis.
 
I understand that you want to store a string whenever RELAY 46 goes active.  This would be the "First In" part of the system.

Questions:
  • Under what conditions do you want to remove the string data from your FIFO?  I am asking about the "First Out" part of the system.
  • How many messages do you need to store in your FIFO?  The Fx2424 has several ways to store string data:
    • A$..Z$  Only 26 strings, but 70-charaters in length.
    • non-volatile memory using LOAD_EEP$/SAVE_EEP$.  You can have up to 300 40-character strings on the Fx2424.
    • extended file system that can store well over 1,000,000 bytes of string data. Strings length can be variable.
    • DM[] memory if you want to pack and unpack the strings.
    • non-volatile memory using LOAD_EEP/SAVE_EEP  if you want to pack and unpack the strings.
  • How many characters long is the longest string that you need to store?
  • How many different strings will you store? Is each string unique?
I'll make no attempt to answer the questions posed about the program snippet that you supplied as it is the slowest possible way to build a FIFO in TBASIC and it does not handle text strings.  If you can answer my questions, I can show you code that will actually do what you need.

Best regards,

Gary D*ckinson
« Last Edit: February 14, 2017, 04:29:56 PM by garysdickinson »

Lorne Van Dusen

  • Jr. Member
  • Posts: 93
  • I'm a old guy
    • View Profile
Re:Creating a FIFO
« Reply #2 on: February 15, 2017, 06:22:17 AM »
Gary somehow I knew it would be you that answered the question.
I totally understand the concept of a FIFO First in First Out. I don't care if the oldest message gets lost.
The easiest way to explain what I need to do is this. Even though I am using a Text display I cannot use the built in Alarm function as the text display is not normally connected to the plc. it is only connected when the mechanic comes along for service or to repair a shut down then when they are finished they take the display with them as it is used as a tool.
What I need to accomplish is a way of storing say up to 15 (up to 20 Character Long Messages) that can be retrieved either by logging into the PLC and viewing them or by retrieving them and viewing them on the text display.
I can create multiple screens in the text display that will show the messages in order. Or I can use one screen and increment or decrement a counter to move the various messages into the same variable message.
The message to be moved into the FIFO will be A$
However I have the problem of that I have already used up most of the Variable strings A$ to K$ as well as W$ to Z$ so I actually don't have 15 spare variable strings.

Regards
Lorne :D

garysdickinson

  • Hero Member
  • Posts: 502
  • Old PLC Coder
    • View Profile
Re:Creating a FIFO
« Reply #3 on: February 15, 2017, 08:12:47 AM »
Lorne,

I have a test code for a FIFO that handles string data using the non-volatile memory and the LOAD_EEP$ and SAVE_EEP$ mechanism. The code dates back to 2012 and I was using it for a Nano-10 / GSM MODEM project.

However, this test code is solving a much more complex problem than you have and I would like to simplify the code down to what you need. So give me a day or so to edit out all of the useless stuff.

Gary D.

garysdickinson

  • Hero Member
  • Posts: 502
  • Old PLC Coder
    • View Profile
Re:Creating a FIFO
« Reply #4 on: February 15, 2017, 12:33:56 PM »
Lorne - I've attached a PLC program to this message that demonstrates a simple FIFO mechanism for storing string messages in non-volatile memory.

I would suggest that you play with this program using the i-TRiLOGI simulator until you can understand how it works.

I use the #Define mechanism exhaustively to create readable names for variables.  So the first thing that you need to do is open one of the custom functions and open the #Define table.

#   #Define Name   Value
1   ENQ_Offset      DM[1]
2   DEQ_Offset      DM[2]
3   FIFO_Cnt         DM[3]
4   FIFO_Size         5
5   FIFO_StartAddr   1

You will note that use 3, 16-bit integer variables to manage the FIFO. ENQ_Offset is used when a message is added, enqueued, to determine where in non-volatile memory to write the data.
DEQ_Offset is used when a message is removed, dequeued, to determine where in non-volatile memory to retrieve the data.
FIFO_Cnt is the current number of message stored in the FIFO

The FIFO_Size define determines the FIFO size in the count of messages.  For my test code, it is 5.  Adjust as needed
FIFO_StartAddr is the starting address in non-volatile memory. For my test code the starting address is 1.  Adjust as needed.

There are 2 RELAYs that you can use in both ladder logic and in custom functions to determine the current FIFO status:
    [*]MsgFifoEmpty - set when FIFO is empty and holds no messages
    [*]MsgFifoFull - set when the FIFO is full and if a new message is equeued then the oldest message in the FIFO will be lost
    [/list]
    This is the entire custom function to enqueue a string in the FIFO:
    Code: [Select]
    ' EnqueueMsg - function to enqueue an array of 40 character messages into the Message FIFO
    '
    '
    ' On entry A$ holds the string to enqueue
    '  This FIFO uses the SAVE_EEP$ statement to store data in the non-volatile memory.
    '  The message length is limited to 40 characters
    '
    '  On exit: The A$ will be written to the FIFO  
    '         The FIFO_FULL and FIFO_EMPTY RELAYS may be affected.
    '         The DM[] variables ENQ_Offset, DEQ_Offset and FIFO_CNT may be updated.
    '         If the FIFO is full then the B$ variable will be written with
    '             the oldest message in the FIFO to make room for the new message


    '  If the FIFO is FULL, then discard oldest message in the FIFO
    '
    IF TestIO(MsgFifoFull)
       ' the FIFO is full, but Lorne is OK with overwriting old messages
       '  so we will discard the oldest to make room for the newest message.
       '  This action overwrite the value of B$
       '
       Call DequeueMsg
    ENDIF

    SAVE_EEP$ A$,FIFO_StartAddr + ENQ_Offset   ' write one entry into the FIFO      

    ' Update FIFO index, counter and flags
    '
    ENQ_Offset = (ENQ_Offset + 1) MOD FIFO_Size
    FIFO_Cnt = FIFO_Cnt + 1

    IF FIFO_Cnt = FIFO_Size
       SetIO MsgFifoFull         ' the FIFO is full and cannot accept more data
    ENDIF

    ClrIO MsgFifoEmpty            ' FIFO cannot be empty

    This is the entire custom function to enqueue a string in the FIFO:
    Code: [Select]
    ' DequeueMsg - function to dequeue String data from the String FIFO
    '
    '  On exit:
    '          B$ will hold the oldest string in the FIFO.
    '         The FIFO_FULL and FIFO_EMPTY RELAYS may be affected.
    '         The DM[] variables ENQ_Offset, DEQ_Offset and FIFO_CNT may be updated.


    '  Return message from FIFO in B$
    '
    IF TestIO(MsgFifoEmpty)
       ' the FIFO is empty and there were no values to remove from the FIFO
       ' Just return a NULL String
       '
       B$ = ""
    ELSE
       ' the FIFO was not empty, so return oldest message in B$]
       '
       B$ = LOAD_EEP$(FIFO_StartAddr + DEQ_Offset)

       ' Update index, FIFO count and flags
       '
       DEQ_Offset = (DEQ_Offset + 1) MOD FIFO_Size
       FIFO_Cnt = FIFO_Cnt - 1

       IF FIFO_Cnt = 0
          SetIO MsgFifoEmpty   ' the FIFO is empty and there is no more data to remove
       ENDIF

       ClrIO MsgFifoFull         ' FIFO cannot be full

    ENDIF

    This approach may seem like lot of work, but it actually takes very few lines of code and executes very rapidly.  This is classic computer science done without the help of modern computer languages.

    Best regards,

    Gary D*ckinson
    « Last Edit: February 15, 2017, 01:00:25 PM by garysdickinson »

    Lorne Van Dusen

    • Jr. Member
    • Posts: 93
    • I'm a old guy
      • View Profile
    Re:Creating a FIFO
    « Reply #5 on: February 16, 2017, 02:38:14 PM »
    Gary I really appreciate the quick turn around for your response on this subject.
    I created a Custom Function (FIFO) that is called from the rising edge of relay 146
    So when a message is created and stored into A$ the relay 146 is turned on
    I then inserted your sample program into the Custom Function & removed your Call DequeueMsg
    I then included #   #Define Name   Value
    1   ENQ_Offset      DM[1700] // DM1700 to DM1800 not used anywhere else
    2   DEQ_Offset      DM[1701]
    3   FIFO_Cnt         DM[1702]
    4   FIFO_Size         15
    5   FIFO_StartAddr   10

    Also I changed your B$ to E$ as I was already using B$
    When I run the simulator and Type in a message into A$ then I trigger Relay 147 nothing happens
    I tried it many times and the value in FIFO_Cnt never changes
    I see you use an Old Version of software and use ' for Rem Statements I found using the // makes the program easier to read. Can you please look over the program below & let me know where I went wrong.
    Also when it is working how can I read the Stored Messages?

    Actual program
    // This function is used to store the latest 15 Fault Messages the the PLC has detected.
    // This Custom Function was created with the expert help from GARRY DICKINSON

    // EnqueueMsg - function to enqueue an array of 40 character messages into the Message FIFO

    // On entry A$ holds the string to enqueue
    // This FIFO uses the SAVE_EEP$ statement to store data in the non-volatile memory.
    // The message length is limited to 40 characters

    // On exit: The A$ will be written to the FIFO
    //         The FIFO_FULL and FIFO_EMPTY RELAYS may be affected.
    //        The DM[] variables ENQ_Offset, DEQ_Offset and FIFO_CNT may be updated.
    //         If the FIFO is full then the E$ variable will be written with
    //           the oldest message in the FIFO to make room for the new message


    //  If the FIFO is FULL, then discard oldest message in the FIFO

    IF TestIO(MsgFifoFull) // the FIFO is full so we will discard the oldest to make room for the newest message.
                         // This action will overwrite the value of E$
       
    ENDIF

    SAVE_EEP$ A$,FIFO_StartAddr + ENQ_Offset   // Write one entry into the FIFO      

                                     // Update the FIFO index, counter and flags

    ENQ_Offset = (ENQ_Offset + 1) MOD FIFO_Size
    FIFO_Cnt = FIFO_Cnt + 1

    IF FIFO_Cnt = FIFO_Size
       SetIO MsgFifoFull         // the FIFO is full and cannot accept more data
    ENDIF

    ClrIO MsgFifoEmpty           // FIFO cannot be empty


    // This is the entire custom function to enqueue a string in the FIFO:
    // Code:

    // DequeueMsg - function to dequeue String data from the String FIFO

    // On exit:  E$ will hold the oldest string in the FIFO.
    //        The FIFO_FULL and FIFO_EMPTY RELAYS may be affected.
    //        The DM[] variables ENQ_Offset, DEQ_Offset and FIFO_CNT may be updated.


    // Return message from FIFO in E$
     
    IF TestIO(MsgFifoEmpty)
       // the FIFO is empty and there were no values to remove from the FIFO
       // Just return a NULL String
       //
       E$ = ""
    ELSE
       // the FIFO was not empty, so return oldest message in E$]
       
       E$ = LOAD_EEP$(FIFO_StartAddr + DEQ_Offset)

       // Update index, FIFO count and flags
       
       DEQ_Offset = (DEQ_Offset + 1) MOD FIFO_Size
       FIFO_Cnt = FIFO_Cnt - 1

       IF FIFO_Cnt = 0
          SetIO MsgFifoEmpty   // the FIFO is empty and there is no more data to remove
       ENDIF

       ClrIO MsgFifoFull        // FIFO cannot be full
    ENDIF

    garysdickinson

    • Hero Member
    • Posts: 502
    • Old PLC Coder
      • View Profile
    Re:Creating a FIFO
    « Reply #6 on: February 16, 2017, 05:09:27 PM »
    Lorne, I can't debug your program because you did not send it to me.

    In your sample code you "went wrong" by removing the call the "DequeueMsg" in the "EnqueueMsg" custom function.  The entire purpose of the call the DequeueMsg was to handle the case where the FIFO is full of messages and you need to enqueue another message.  In order to make room for the new message, the call to DequeueMsg removed the oldest message from the FIFO.  You MUST put the code back.  This code was critical to maintaining the 3 numberic variables that manage the
    FIFO.  BUT this is not why the code is not working in your environment.

    I can't tell you why the FIFO_Cnt value stored in DM[1702] is not changing without having your entire PLC program. But my guess is that the EnqueueMsg is not getting called.  I suggest that you add a breakpoint into the CF that you wrote to handle RELAY 146 and add a breakpoint in the EnqueueMsg and use on-line monitoring to look at the code running on real hardware.

    I assume that you picked up the Init CF from my test code and added it to your code to ensure that the FIFO is properly initialized.

    In regards to how do you "read the stored messages", you call the DequeueMsg CF and each call to this function will return the oldest message in B$ (you changed it to E$ in your version). Once all of the messages have been read, subsequent calls to DequeuMsg will return "" in B$.  The FIFO is now empty.

    It is possible to write a non-destructive method to peak at the messages without removing them from the FIFO.

    Best regards,

    Gary D*ckinson






    « Last Edit: February 16, 2017, 05:10:37 PM by garysdickinson »

    Lorne Van Dusen

    • Jr. Member
    • Posts: 93
    • I'm a old guy
      • View Profile
    Re:Creating a FIFO
    « Reply #7 on: February 22, 2017, 06:09:16 AM »
    Gary i guess I was in too much of a rush to test the program.
    Once I took the time and studied what you had sent me I realized what I did wrong.
    Your program works well.
    Again thanks for your help  :)

    garysdickinson

    • Hero Member
    • Posts: 502
    • Old PLC Coder
      • View Profile
    Re:Creating a FIFO
    « Reply #8 on: February 22, 2017, 09:59:12 AM »
     Lorne,

    Great to hear that you have been able to figure things out.  Debugging PLC programs is nearly impossible.

    Let me know if there is anything else that I can help you with.  

    Gary D