Examples - SMTP Example

Top  Previous  Next

//-----------------------------------------------------------------------------
// smtp_example.vpl, created 2009-11-11 09:16
//
// An example of how SMTP can be used to monitor a liquid container
//
// Illustration:
//
//             |o|   <- supply flow monitor
//             +-+   <- top valve
//        |    | |    |
//        |          -| <- High level
//        |           |
//        |          -| <- Low level
//        +----+ +----+
//             +-+   <- bottom valve
//             |o|   <- drain flow monitor
//
// If any 1-Wire temperature sensors are connected, data will be collected and
// sent by email.
//-----------------------------------------------------------------------------
INCLUDE rtcu.inc
 
VAR_INPUT
  level_high   : BOOL; | liquid is above high level
  level_low   : BOOL; | liquid is above low level
  flow_supply  : INT; | amount of liquid running into tank
  flow_drain   : INT; | amount of liquid running out of tank
END_VAR;
 
VAR_OUTPUT
  valve_top    : BOOL; | active open
  valve_bottom : BOOL; | active open
END_VAR;
 
VAR
  // Mail configuration
  receiver     : STRING := "mail@domain.com";
 
  clock        : clockGet;           // Reads the builtin Clock
  cnv_time     : clockLinsecToTime; // Converts number to date format
END_VAR;
 
//---------------------------------------------------------------------------
// THREAD : tb_SendDataFile
//---------------------------------------------------------------------------
THREAD_BLOCK tb_SendDataFile;
VAR_INPUT
  filename     : STRING;
END_VAR;
VAR_OUTPUT
  status       : INT; // mail send status
  mail         : INT;
END_VAR;
VAR
  rc           : INT;
END_VAR;
  status := 1000;
 
  // Start a new e-mail
  mail := smtpNew(Receiver := receiver, Subject := "Data collected.");
  // Add text to the e-mail
  smtpAddText(Handle := mail, Message := "Data has been collected from ");
 
  cnv_time(linsec := fsFileGetCreateTime(name := filename));
  // date
  smtpAddText(Handle := mail, Message := intToStr(v := cnv_time.day));
  smtpAddText(Handle := mail, Message := "-" + intToStr(v := cnv_time.month));
  smtpAddText(Handle := mail, Message := "-" + intToStr(v := cnv_time.year));
  // time
  smtpAddText(Handle := mail, Message := " " + intToStr(v := cnv_time.hour));
  smtpAddText(Handle := mail, Message := ":" + intToStr(v := cnv_time.minute));
  smtpAddText(Handle := mail, Message := ":" + intToStr(v := cnv_time.second));
 
  smtpAddText(Handle := mail, Message := " to ");
 
  cnv_time(linsec := clockNow());
  // date
  smtpAddText(Handle := mail, Message := intToStr(v := cnv_time.day));
  smtpAddText(Handle := mail, Message := "-" + intToStr(v := cnv_time.month));
  smtpAddText(Handle := mail, Message := "-" + intToStr(v := cnv_time.year));
  // time
  smtpAddText(Handle := mail, Message := " " + intToStr(v := cnv_time.hour));
  smtpAddText(Handle := mail, Message := ":" + intToStr(v := cnv_time.minute));
  smtpAddText(Handle := mail, Message := ":" + intToStr(v := cnv_time.second));
 
  // Attach a file to the e-mail
  smtpAddAttachment(Handle := mail, Filename := filename);
 
  // Send the e-mail
  rc := smtpSendX(Handle := mail);
  IF rc <> 0 THEN
    DebugFmt(message := "Failed to send data file, error code '\1'.", v1 := rc);
  ELSE
    // monitor process
    REPEAT
        status := smtpStatus(Handle := mail);
    UNTIL status < 0
    END_REPEAT;
    DebugMsg(message := "data mail sent.");
  END_IF;
  // Delete file
  fsFileDelete(name := filename);
  DebugMsg(message := "file '" + filename + "' deleted.");
END_THREAD_BLOCK;
 
//---------------------------------------------------------------------------
// SendDataMail instant, used by temperature thread.
//---------------------------------------------------------------------------
VAR
  SendMail : tb_SendDataFile;
END_VAR;
 
//---------------------------------------------------------------------------
// THREAD : tb_temperature_monitor
//---------------------------------------------------------------------------
THREAD_BLOCK tb_temperature_monitor;
VAR
  next_check  : DINT := 0;
  fd          : FILE;
  fname       : STRING := "data.csv";
  next_send   : DINT;
  cur_row    : DINT := 0;
  owCount     : INT;
  value       : DINT;
  new_file    : BOOL := TRUE;
  i          : INT;
  str         : STRING;
END_VAR;
 
  DebugMsg(message:= "Temperature monitor started.");
 
  WHILE TRUE DO
    // Create new data file, on new collection.
    IF new_file THEN
        new_file := FALSE;
 
        // Delete any old file
        fsFileDelete(name := fname);
 
        // Create file
        fd :=fsFileCreate(name := fname);
 
        // Write header row followed by a new line to file
        fsFileWriteStringNL(fd := fd, str := "$"Time$";$"Average$";$"Values ->$"");
 
        next_send := clockNow() + 300; // send each 5th minute
        cur_row := 2;
    END_IF;
 
     // Collect data
    IF next_check < clockNow() THEN
        next_check := clockNow() + 10; // monitor each 10 second.
 
        // Search for One-Wire devices
        owCount := owSearch(family := 1);
 
        // get system clock
        clock();
        // write check time in first column
        fsFileWriteString(fd := fd, str := "$"" + intToStr(v := clock.day));
        fsFileWriteString(fd := fd, str := "-" + intToStr(v := clock.month));
        fsFileWriteString(fd := fd, str := "-" + intToStr(v := clock.year));
        fsFileWriteString(fd := fd, str := " " + intToStr(v := clock.hour));
        fsFileWriteString(fd := fd, str := ":" + intToStr(v := clock.minute));
        fsFileWriteString(fd := fd, str := ":" + intToStr(v := clock.second) + "$"");
 
        // generate average cell recognized by Microsoft Excel and OpenOffice Calc
        str := strFormat(format := ";$"=SUM(C\4:O\4)/\1$"", v1 := owCount, v4 := cur_row);
        // write sum function
        fsFileWriteString(fd := fd, str := str);      
 
        // Write found temperatures
        FOR i := 1 TO owCount DO
          value := owTempGet(device := SINT(i));
           // write data to file
          fsFileWriteString(fd := fd, str := ";$"");
          fsFileWriteString(fd := fd, str := dintToStr(v := value / 100));
          fsFileWriteString(fd := fd, str := ",");
          fsFileWriteString(fd := fd, str := dintToStr(v := value MOD 100));
          fsFileWriteString(fd := fd, str := "$"");
        END_FOR;
 
        // Write newline
        fsFileWriteString(fd := fd, str := "$N");
        cur_row := cur_row + 1;        
 
        // wait 5 second before next check
        Sleep(delay := 5000);
    END_IF;
 
     // Time to send data
    IF next_send < clockNow() THEN
        fsFileClose(fd := fd);
 
        IF SendMail._running THEN
          DebugMsg(message := "Waiting for last mail to finish.");
          WHILE SendMail.status >= 0 AND SendMail._running DO
              DebugFmt(message := "Progress : \1", v1 := SendMail.status);
              Sleep(delay := 500);
          END_WHILE;
      END_IF;
 
        // Rename file
        fsFileRename(name_old := fname, name_new := "temp.csv");
        // send the file as an attachment
        SendMail(filename := "temp.csv");
 
      new_file := TRUE;
    END_IF;
END_WHILE;
END_THREAD_BLOCK;
 
//---------------------------------------------------------------------------
// THREAD_BLOCK : tb_level_monitor
//---------------------------------------------------------------------------
THREAD_BLOCK tb_level_monitor;
VAR
  trig_supply : RF_TRIG;
  trig_drain : RF_TRIG;
  trig_high   : RF_TRIG;
  trig_low  : RF_TRIG;
 
  message     : STRING;
  subject     : STRING;
  send_msg    : BOOL;
 
  rc          : INT;
END_VAR;
 
  DebugMsg(message := "Level monitor started.");
 
  // open vales according to current state
  IF NOT level_high THEN
    valve_top := ON;
  END_IF;
 
  IF level_low THEN
    valve_bottom := ON;
  END_IF;
 
  // skip initial level warnings
  trig_high(trig := level_high);
trig_low(trig := level_low);
  trig_drain(trig := flow_drain > 10);
  trig_supply(trig := flow_supply > 10);
 
  WHILE TRUE DO
    send_msg := FALSE;
 
    trig_high(trig := level_high);
    trig_low(trig := level_low);
 
    trig_drain(trig := flow_drain > 10);
    trig_supply(trig := flow_supply > 10);
 
     // check high level
    IF trig_high.rq THEN
        subject := "WARNING: Liquid level critical high";
        message := subject + ", closing top valve.";
        valve_top := OFF;
        send_msg := TRUE;
    ELSIF trig_high.fq THEN
        subject := "NOTIFY: Liquid level below critical high";
        message := subject + ", opening top valve.";
      valve_top := ON;
        send_msg := TRUE;
   END_IF;
 
     // check low level
    IF trig_low.fq THEN
        subject := "WARNING: Liquid level critical low";
        message := subject + ", closing bottom valve.";
      valve_bottom := OFF;
      send_msg := TRUE;
    ELSIF trig_low.rq THEN
        subject := "NOTIFY: Liquid level above critical low";
        message := subject + ", opening bottom valve.";
        valve_bottom := ON;
        send_msg := TRUE;
    END_IF;
 
    IF send_msg THEN
        DebugMsg(message := message);
        // Send an e-mail
        rc := smtpSend(Receiver := receiver, Subject := subject, Message := message);
        IF rc <> 0 THEN
          DebugFmt(message := "Failed to send message, error code '\1'.", v1 := rc);
      END_IF;
   END_IF;
 
     // monitor supply and drain pipes
    IF trig_supply.fq AND valve_top THEN
        subject := "WARNING: supply pipe clogged";
        message := subject + ", no action taken.";
        DebugMsg(message := message);
        // Send an e-mail
        rc := smtpSend(Receiver := receiver, Subject := subject, Message := message);
        IF rc <> 0 THEN
          DebugFmt(message := "Failed to send message, error code '\1'.", v1 := rc);
      END_IF;
    END_IF;
    IF trig_drain.fq AND valve_bottom THEN
        subject := "WARNING: drain pipe clogged";
        message := subject + ", no action taken.";
        DebugMsg(message := message);
        // Send an e-mail
        rc := smtpSend(Receiver := receiver, Subject := subject, Message := message);
        IF rc <> 0 THEN
          DebugFmt(message := "Failed to send message, error code '\1'.", v1 := rc);
       END_IF;
     END_IF;
 
     // wait 2 seconds before next scan
    Sleep(delay := 2000);
 END_WHILE;
END_THREAD_BLOCK;
 
//---------------------------------------------------------------------------
// PROGRAM: smtp_example
//---------------------------------------------------------------------------
PROGRAM smtp_example;
VAR
  rc                : INT;
 last_signal_time  : DINT := 0;
temp_monitor    : tb_temperature_monitor;
  level_monitor   : tb_level_monitor;
END_VAR;
 
  DebugMsg(message := "Initializing.");
  // Open media
  rc := fsMediaOpen(media := 0);
  DebugFmt(message := "fsMediaOpen = \1", v1 := rc);
 
  // Controls power to the GSM module
  rc := gsmPower(power := ON);
  DebugFmt(message := "gsmPower = \1", v1 := rc);
 
  // Open/activate GPRS connection.
  rc := gprsOpen();
  DebugFmt(message := "gprsOpen = \1", v1 := rc);
 
  // Set SMTP configuration
  smtpSetConfig(
    IP :="mail.domain.com",
    Port := 25,
    Sender := "tank_monitor@domain.com",
    Authenticate := OFF
  );
 
  // Open the smtp interface
  rc := smtpOpen();
  DebugFmt(message := "smtpOpen = \1", v1 := rc);
 
  DebugMsg(message := "Starting threads.");
  temp_monitor();
  level_monitor();
 
  DebugMsg(message := "Ready");
BEGIN
  IF last_signal_time < clockNow() THEN
    last_signal_time := clockNow() + 10; // check again in 10sec
 
     // Check if GPRS connection is established
    IF NOT gprsConnected() THEN
        DebugMsg(message := "WARNING: no connection to GPRS, sending mails not posible.");
    END_IF;
 
    IF NOT temp_monitor._running THEN
        DebugMsg(message := "ERROR: Temperature monitor not running, starting.");
        temp_monitor();
   END_IF;
 
    IF NOT level_monitor._running THEN
        DebugMsg(message := "ERROR: Level monitor not running, starting.");
      level_monitor();
    END_IF;
 END_IF;
END;
END_PROGRAM;