/// <summary> /// Check the email agains the time of day and day of week criteria /// </summary> /// <returns></returns> static bool CheckEmailTODDOW(string emailAddress) { bool result = false; EmailOptions option = EmailAddresses.GetEmailOptions(emailAddress); if (null != option) { DateTime now = DateTime.Now; if (option.AllTheTime) { result = true; } else { // First check the day of week int day; for (day = 0; day < 7; day++) { if (option.DaysOfWeek[day]) { if (day == (int)now.DayOfWeek) { break; } } } if (day < 7) { // OK, the day was correct. Let's look at the time. Can't just compare DateTime because we never look at the date if (now.Hour == option.StartTime.Hour) { // need to check minutes too if (now.Minute >= option.StartTime.Minute) { result = true; // tentative } if (option.EndTime.Minute > now.Minute) { result = false; // It was greater than start, but also greater than end - unusual, but.... } } else if (now.Hour > option.StartTime.Hour) { result = true; } else { } // less than remains false } } } return(result); }
// Here we have a list of frames (sorted in picture date/time). // We have all the information necessary to actually send the emails, so we // might as well do it here. // We may have multiple email recipients. Each email recipient may want 0 to many pictures attached. // We may have many more frames than the recipient wants. In that case we first sort out the // interesting ones. We always start an email with the first interesting picture. From there // the following ones may or may not be interesting. The ones with the highest priority (type Door) // are included before others (in the limited email space). From there we prefer interesting // pictures. If we have room we include the others.. // Note type frames list is in sorted order since it was copied from a SortedList public override void ProcessAccumulatedFrames(List <Frame> frames) { // The priority and interesting list contain indexes into the frames list List <int> priority = new List <int>(); // door events have high priority, everything else, not List <int> interesting = new List <int>(); // frames with objects but not "priority // The collection of area descriptions does not depend on the number of outgoing pictures. // It depends on all the pictures. Each picture may have zero or more than, each object may // be in a different area. Only frames with Interesting Objects have areas, and therefore descriptions HashSet <string> descriptions = new HashSet <string>(); // First, let's get lists of priority and interesting indexes int i = 0; foreach (var frame in frames) { if (frame.Interesting != null && frame.Interesting.Count > 0) { foreach (var io in frame.Interesting) { descriptions.Add(io.Area.AOIName + ": " + io.FoundObject.Label); if (io.Area.AOIType == AOIType.Door) { priority.Add(i); break; } else { if (!priority.Contains(i)) // a frame may have interesting and priority objects, but only add it to one list { descriptions.Add(io.Area.AOIName + ": " + io.FoundObject.Label); interesting.Add(i); } } } } i++; } // This is out of place codewise, but.... if (priority.Count > 0) { // signal the camera to wait the full interval regardless of door events in following frame since we already reported a door object frames[0].Item.CamData.LastAccumulateType = AOIType.Door; } else { frames[0].Item.CamData.LastAccumulateType = AOIType.IgnoreObjects; // respect the email interval } // Now, let's get the list of email addresses to send to based on time of day, day of week, normally just 1 to a small number Dictionary <string, string> emailAddresses = new Dictionary <string, string>(); // Yes, these could be combined, but for debugging we look at them separately // Go through the "priority"/door frames we already selected and add the related email addresses foreach (int index in priority) { foreach (var ii in frames[index].Interesting) { foreach (var email in ii.Area.Notifications.Email) { emailAddresses[email] = email; } } } // Go through the "interesting "non-door" frames we already selected and add the related email addresses foreach (int index in interesting) { foreach (var ii in frames[index].Interesting) { foreach (var email in ii.Area.Notifications.Email) { emailAddresses[email] = email; } } } // Now, go through all email addresses to see if we are in a period we would at least consider sending emails to bool removedOne; do { removedOne = false; foreach (string emailAddress in emailAddresses.Values) { bool timeOK = CheckEmailTODDOW(emailAddress); if (!timeOK) { emailAddresses.Remove(emailAddress); removedOne = true; break; // go through the entire list again - it may have changed (not the most efficient, but a small list so.... } } } while (emailAddresses.Count > 0 && removedOne); // ok, we know that we MAY want to notify some email address, but each // address may have a different cool down time, so we cull the list further (yes we could do it differently) do { removedOne = false; foreach (string emailAddress in emailAddresses.Values) { EmailOptions option = EmailAddresses.GetEmailOptions(emailAddress); if (null != option) { TimeSpan span = DateTime.Now - option.CoolDown.LastSent; if (span.TotalMinutes < option.CoolDown.CooldownTime) { Dbg.Write("Email address in cool down: " + emailAddress); emailAddresses.Remove(emailAddress); removedOne = true; break; // go through the entire list again - it may have changed (not the most efficient, but a small list so....) } } } } while (emailAddresses.Count > 0 && removedOne); // OK, we have FINALLY culled the list of email addreses to send to. // Now, we need to go through email recipients and then select the frames we want to send and then // send them. SortedList <DateTime, Frame> outgoing = new SortedList <DateTime, Frame>(); foreach (string addr in emailAddresses.Values) { outgoing.Clear(); EmailOptions opt = EmailAddresses.GetEmailOptions(addr); if (null != opt) { // The follwing is done the way it is done for debugging. // TODO: Reorganize int maxPic = opt.NumberOfImages; if (priority.Count >= maxPic) { // No decisions to make, just use priority until reached the max (debugging) for (i = 0; i < maxPic; i++) { outgoing.Add(frames[priority[i]].Timestamp, frames[priority[i]]); } } else { // Here we have some decisions to make. First add priority frames for (i = 0; i < priority.Count; i++) { outgoing[frames[priority[i]].Timestamp] = frames[priority[i]]; } // In addition to the priority, add frames with "interesting" objects for (i = 0; i < interesting.Count && outgoing.Count < maxPic; i++) { outgoing[frames[interesting[i]].Timestamp] = frames[interesting[i]]; } // OK, we had some space left after the priority and interesting frames // If there ares some frames remaining, add from the beginning of the frames. // Note that the ones at the beginning of the frame list that aren't priority or // interesting will be favored over the ones later in the list. This may or may not be // good thing. That is, you may want to favor picures of people approaching the door // over pictures of them leaving. You are more likely to get faces for one. for (i = 0; i < frames.Count && outgoing.Count < maxPic; i++) { if (!(priority.Contains(i)) && !(interesting.Contains(i))) // No duplication of the ones we added6 { outgoing.Add(frames[i].Timestamp, frames[i]); } } } // end else } // end we do have an option (which we should) // We copy off the list of files and the list of descriptions because we are // going to clear these lists for the next email address string[] theFiles = new string[outgoing.Count]; // also, we may be resizing the images int count = 0; foreach (Frame frame in outgoing.Values) { theFiles[count] = frame.Item.PendingFile; ++count; } string[] theDescriptions = new string[outgoing.Count]; descriptions.CopyTo(theDescriptions); // OK, here we have collected all the frames for this email address. // It is time to resize the pictures if necessary. Resizing is based on the email // address since photos going to a phone should be smaller than those going to a PC, etc. count = 0; foreach (string file in theFiles) { if (opt.SizeDownToPercent != 100) { theFiles[count] = ResizeImage(theFiles[count], opt.SizeDownToPercent); ++count; } } // theFiles has both resized and not resized files Task.Run(() => SendEmail(addr, theFiles, theDescriptions)); } }