Internet PLC Forum

General => Technical support => Topic started by: Lorne Van Dusen on February 14, 2017, 01:19:06 PM

Title: Creating a FIFO
Post by: Lorne Van Dusen 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.

Title: Re:Creating a FIFO
Post by: garysdickinson 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:
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
Title: Re:Creating a FIFO
Post by: Lorne Van Dusen 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
Title: Re:Creating a FIFO
Post by: garysdickinson 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.
Title: Re:Creating a FIFO
Post by: garysdickinson 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:
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
Title: Re:Creating a FIFO
Post by: Lorne Van Dusen 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
Title: Re:Creating a FIFO
Post by: garysdickinson 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






Title: Re:Creating a FIFO
Post by: Lorne Van Dusen 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  :)
Title: Re:Creating a FIFO
Post by: garysdickinson 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