Email & Queueing


Recommended Posts

I would like to be able to setup an email from within DAQFactory, have the software email out when a threshold is reached, and if the network connectivity is down for whatever reason (bad weather, lost connection, etc), keep the email in some sort of queue so that the system retries every x seconds.

 

For instance:

  have a script that monitors myChan[0], and if it exceeds a value like 250, send email.  If the email fails, wait 600 seconds and try again, and continue this loop for 24 hours, or whenever the email out is successful, whichever comes first.

 

Is this possible and if so, could you give an example of how to accomplish this?

Link to comment
Share on other sites

This can be done in 5.91 using the new CEmail object.  You can't do it in older versions using the internal email. variables because email.send() is asynchronous, meaning it starts the email, but returns immediately so you can't check to see if the email send was successful except by looking in the command / alert window.  In 5.91 (or really 5.90.9) we introduced the CEmail object which allows you to create an object for handling an email.  Details on using this object are in the user's guide. The advantage and purpose of CEmail was to avoid collisions from using a single global email function (email.send()) to send multiple emails.  The send() member function of CEmail works slightly differently than email.send() in that it is synchronous or blocking.  When you call send(), it doesn't return until the email is either successfully sent, or there is an error.  If there is an error, an error is thrown in the script which you can then catch using try/catch:

 



try
   myEmail.send()
   ? "Email successful"
catch()
   ? "Email failed: " + strLastError
endcatch


OK, that's the overview, now the details.  To implement a queue you simply need to maintain an array of objects with the details for each email.  Since you want an email to be removed from the queue after 24 hours, we will need to also store the that time, so using our own object is going to make it better.  

 

First, create a simple object to hold the email object and the time the email was first sent.  This can go in an auto-start sequence, or I prefer to put class definitions in their own sequence with no other code so that I can rerun the sequence as I change the class.  Just make sure the sequence runs once at startup:

 



class CEmailOut
   local emailOb
   local initialTime
endclass


We'll also need a global variable to hold the queue, so declare that somewhere:

 



global emailQueue


Next, create a function to send an email.  This is the function you'd call to try sending an email, and if it fails, put it in the queue (so the one you'd call in your myChan event).  For clarity I'm going to skip much of the script for initializing a CEmail object with usernames, passwords, email body's, etc.:

 



function sendEmail(string message)
   private emailOut = new(CEmailOut)
   emailOut.initialTime = systime()
   private em = new(CEmail) // put in private for clarity
   emailOut.emailOb = em
   // initialize em with em.username, etc.
   em.body = message
   // ... more initilize code here


   // now try and send the email using another function we will create:
   if  (!sendEmailOb(emailOut))
      // email failed, so add to queue:
      emailQueue.addValue(emailOut)
   endif
   // if it sent, we can just let emailOut go out of scope and it all goes away.


Now we need a function to send the email when passed our CEmailOut object.  This function will return TRUE if successful, and FALSE if not:

 



function sendEmailOb(emailOut)
   try
      emailOut.emailOb.send()
      return(1)
   catch()
      ? strLastError // you may want to do something with the error message
      return(0)
   endcatch


Finally we need a sequence that is always running that processes the queue, in this example, every 5 minutes:

 



while(1)
   for (private i = numrows(emailQueue)-1, i >= 0, i--)
      // its always best to process arrays backwards if you are going to remove elements from the array in the loop
      private emailOut = emailQueue[i]
      if (sendEmailOb(emailOut))
         emailQueue.removeAt(i) // success, so remove from queue
         continue
      else // failed
         // check time:
         if (emailOut.initialTime < systime() - 86400)
            emailQueue.removeAt(i) // success, so remove from queue
            continue
         endif
      endif
    endfor
    delay(300)
endwhile


I'd probably run that sequence at priority 0, or maybe 1.

 

That should do the job, though I will admit that I wrote this off the cuff and didn't actually test it, so you may have to debug it a little, and of course adapt it for your application, but this should get you off to a good start.               

Link to comment
Share on other sites

  • 2 months later...

I'm getting around to trying this setup and keep getting an error.  I'm not a programmer, so it's taking me a bit to understand the above.

 

   First, do both of the functions above go in a single sequence, or should they be separated into two?

 

   I'm getting an error when calling sendEmail("TEST") for the line if(!sendEmailOb(emailOut)) stating that the channel or function is not defined.  I've checked the syntax and cannot find any errors....any thoughts?

 

   Third, in the function you described, you have a line, private em = new(CEmail)...should this read private em = new(CEmailOut)?

 

   Lastly,  is the em.body the correct way to set the body of the message or would it be similar to the actual email command where it's set as em.strBody?

 

Thanks

Link to comment
Share on other sites

1) The last three are separate sequences.  The first box is just describing the process and that code is already in the other code.  The second and third box of code go in an auto-start sequence.

2) you get the error because the box that has "function sendEmailOb(emailOut)" has to be in its own sequence named sendEmailOb

3) no.  CEmail is a system object for handling emails.  CEmailOut is a class you create as declared in the second box. 

Link to comment
Share on other sites

Thanks!  I've been able to get this to work to an extent.  I did what you said above and methodically stepped through the process of the functionality.  I added some ? statements to see where it's getting in the process.  Where I stand now, is if I have an internet connection, everything works as expected.  If I don't, I go through the top two sequences, but the statement emailQueue.addValue(emailOut) does not truly put anything in the emailQueue global.  Since it's a global, I added it to the watch window and never see it get populated.  I changed that one statement by removing (emailOut) and replacing it with (1) and it all worked great.  Did you mean to put emailOut in that statement in your original code above, or was that simply an oversight?  If it's supposed to be there, I'll need to figure out why it still won't work.

Link to comment
Share on other sites

I just realized that when I was troubleshooting, I changed the second line in your function SendEmail(string message) sequence from:

 

    private emailOut = new(CEmailOut)        to        global emailOut = new(CEmailOut)

 

AND

 

    emailQueue.addValue(emailOut)            to        emailQueue.addValue(1)

 

With both of the above statements modified, it seems to work as expected.  With the last statement modified, and emailOut left as a private variable, emailOut.initialTime never gets passed, so no matter how long of a delay is set, it always backs out thinking the time has elapsed.

 

Can objects be passed using addValue? or is there something else to it that I'm missing.

 

Thanks

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.