Schedule slots. Programming schedules

Whilst playing around today I found the hidden slot in the Schedule property sheet, which has a composite of other slots under it, if I change it’s flag to untick hidden. I can expose the slot:
BooleanSchedule/schedule/week/monday/day/time/start

…/finish

And then same again for Tue Wed etc

when I composite these to the object in wiresheet I can input a date and time as BAbsTime and it takes the time and populates the schedule, is there a way to make a programobject write these slot directly?

So I believe I tried to do this in a program object before and while it seems to write the start and finish, it didn’t seem to physically register the change to be viewed from the schedule. In BajaScript, you can do an RPC call for the java methods. Let me see what I can do with it this week. It might take a few days as the beginning of the week is pretty busy form me.

Looking at this more, I think you’d be able to do it. But I think you need BTime versus BAbsTime

Thanks Charles. I’ll give that a try later and update what I find

Not only do you need BTime, the time slot that gets created, you’ll need to add a slot called effectiveValue and that’s a baja:StatusBoolean. It’s the way the schedule will add the event to the schedule that you can see and it can read.

  public void onExecute() throws Exception
  {
    BOrd ord = getScheduleOrd();
    if (ord == null || ord.isNull())
      throw new IllegalStateException("Program slot 'scheduleOrd' is null.");

    BComponent target = (BComponent) ord.get();

    BValue schedVal = target.get("schedule");
    if (!(schedVal instanceof BCompositeSchedule))
      throw new IllegalStateException("Target does not contain slot 'schedule' of type schedule:CompositeSchedule.");

    BCompositeSchedule composite = (BCompositeSchedule) schedVal;

    BValue weekVal = composite.get("week");
    if (!(weekVal instanceof BWeekSchedule))
      throw new IllegalStateException("CompositeSchedule.week is not a schedule:WeekSchedule.");

    BWeekSchedule week = (BWeekSchedule) weekVal;

    final String ranges = getRanges();
    final BStatusBoolean desired = getDesiredFromValueSlot();

    if (getClearFirst())
      clearEntireWeek(week);

    if (getApplySunday())    applyToDay(week, "sunday",    ranges, desired);
    if (getApplyMonday())    applyToDay(week, "monday",    ranges, desired);
    if (getApplyTuesday())   applyToDay(week, "tuesday",   ranges, desired);
    if (getApplyWednesday()) applyToDay(week, "wednesday", ranges, desired);
    if (getApplyThursday())  applyToDay(week, "thursday",  ranges, desired);
    if (getApplyFriday())    applyToDay(week, "friday",    ranges, desired);
    if (getApplySaturday())  applyToDay(week, "saturday",  ranges, desired);

    try
    {
      Action exec = target.getAction("execute");
      if (exec != null) target.invoke(exec, null);
    }
    catch (Exception ignored) {}

    System.out.println("ScheduleAdjust updated: " + ord);
  }

  private void clearEntireWeek(BWeekSchedule week)
  {
    clearDay(week, "sunday");
    clearDay(week, "monday");
    clearDay(week, "tuesday");
    clearDay(week, "wednesday");
    clearDay(week, "thursday");
    clearDay(week, "friday");
    clearDay(week, "saturday");
  }

  private void clearDay(BWeekSchedule week, String daySlotName)
  {
    try
    {
      BValue dayObjVal = week.get(daySlotName);
      if (dayObjVal == null) return;

      BObject dayObj = (BObject) dayObjVal;
      BDaySchedule daySchedule = extractDaySchedule(dayObj);

      if (daySchedule != null)
        removeTimeChildren(daySchedule);
    }
    catch (Exception ignored) {}
  }


  private void applyToDay(BWeekSchedule week, String daySlotName, String ranges,
                          BStatusBoolean desired) throws Exception
  {
    BValue dayObjVal = week.get(daySlotName);
    if (dayObjVal == null)
      throw new IllegalStateException("WeekSchedule." + daySlotName + " is null.");

    BObject dayObj = (BObject) dayObjVal;


    BDaySchedule daySchedule = extractDaySchedule(dayObj);
    if (daySchedule == null)
      throw new IllegalStateException("Could not find schedule:DaySchedule under week." + daySlotName +
        " (object type: " + dayObj.getType() + ").");

    String r = (ranges == null) ? "" : ranges.trim();

    if (r.isEmpty() || r.equalsIgnoreCase("closed") || r.equalsIgnoreCase("off"))
      return;

    if (r.equalsIgnoreCase("24/7") || r.equalsIgnoreCase("24x7"))
    {
      addTimeBlock(daySchedule, parseTime("00:00"), parseTime("24:00"), desired);
      return;
    }

    String[] blocks = r.split(",");
    for (int i = 0; i < blocks.length; i++)
    {
      String b = blocks[i].trim();
      if (b.isEmpty()) continue;

      String[] se = b.split("-");
      if (se.length != 2)
        throw new IllegalArgumentException("Bad time range for " + daySlotName + ": '" + b +
          "'. Use 'HH:mm-HH:mm' (comma-separated allowed).");

      BTime start = parseTime(se[0].trim());
      BTime finish = parseTime(se[1].trim());

      addTimeBlock(daySchedule, start, finish, desired);
    }
  }

  private BDaySchedule extractDaySchedule(BObject weekDayObj)
  {
    if (weekDayObj instanceof BDaySchedule)
      return (BDaySchedule) weekDayObj;

    try
    {
      if (weekDayObj instanceof BComplex)
      {
        BValue v = ((BComplex) weekDayObj).get("day");
        if (v instanceof BDaySchedule)
          return (BDaySchedule) v;
      }
    }
    catch (Exception ignored) {}

    return null;
  }


  private void removeTimeChildren(BDaySchedule day)
  {
    BComponent c = (BComponent) day;
    SlotCursor sc = c.getSlots();

    String[] names = new String[8];
    int count = 0;

    while (sc.next())
    {
      Slot s = sc.slot();
      if (s == null) continue;

      String name = s.getName();
      if (name == null) continue;

      if (!name.regionMatches(true, 0, "time", 0, 4))
        continue;

      if (count == names.length)
      {
        String[] bigger = new String[names.length * 2];
        for (int i = 0; i < names.length; i++) bigger[i] = names[i];
        names = bigger;
      }
      names[count++] = name;
    }

    for (int i = 0; i < count; i++)
    {
      try { c.remove(names[i]); }
      catch (Exception ignored) {}
    }
  }


private void addTimeBlock(BDaySchedule day, BTime start, BTime finish, BStatusBoolean desired)
{
  BComponent dayC = (BComponent) day;

  BTimeSchedule ts = new BTimeSchedule();

  ts.set("start", start);
  ts.set("finish", normalizeFinish(finish));

  dayC.add("time?", ts);

  BStatusBoolean ev = new BStatusBoolean(desired.getBoolean(), desired.getStatus());

  ts.add("effectiveValue", ev);
}

  private BTime normalizeFinish(BTime finish)
  {
    if (finish.getHour() == 24 && finish.getMinute() == 0 && finish.getSecond() == 0)
      return BTime.make(0, 0, 0);
    return finish;
  }

  private BStatusBoolean getDesiredFromValueSlot()
  {
    BStatusBoolean sb = getValue();
    if (sb == null) return new BStatusBoolean(true, BStatus.ok);
    return sb;
  }

  private BTime parseTime(String s)
  {
    s = s.trim().toUpperCase();

    boolean pm = s.endsWith("PM");
    boolean am = s.endsWith("AM");
    if (pm || am)
      s = s.replace("AM", "").replace("PM", "").trim();

    String[] parts = s.split(":");
    int h = Integer.parseInt(parts[0].trim());
    int m = (parts.length > 1) ? Integer.parseInt(parts[1].trim()) : 0;

    if (pm && h < 12) h += 12;
    if (am && h == 12) h = 0;

    // allow 24:00
    if (h == 24 && m == 0)
      return BTime.make(24, 0, 0);

    return BTime.make(h, m, 0);
  }
1 Like

Here are the slots that I used:

import java.util.*;              /* java Predefined*/
import javax.baja.nre.util.*;    /* nre Predefined*/
import javax.baja.sys.*;         /* baja Predefined*/
import javax.baja.status.*;      /* baja Predefined*/
import javax.baja.util.*;        /* baja Predefined*/
import com.tridium.program.*;    /* program-rt Predefined*/
import javax.baja.schedule.*;    /* schedule-rt User Defined*/
import javax.baja.timezone.*;    /* baja User Defined*/
import javax.baja.naming.*;      /* baja By Property*/

////////////////////////////////////////////////////////////////
// Getters
////////////////////////////////////////////////////////////////

  public BOrd getScheduleOrd() { return (BOrd)get("scheduleOrd"); }
  public String getRanges() { return getString("ranges"); }
  public boolean getClearFirst() { return getBoolean("clearFirst"); }
  public boolean getApplySunday() { return getBoolean("applySunday"); }
  public boolean getApplyMonday() { return getBoolean("applyMonday"); }
  public boolean getApplyTuesday() { return getBoolean("applyTuesday"); }
  public boolean getApplyWednesday() { return getBoolean("applyWednesday"); }
  public boolean getApplyThursday() { return getBoolean("applyThursday"); }
  public boolean getApplyFriday() { return getBoolean("applyFriday"); }
  public boolean getApplySaturday() { return getBoolean("applySaturday"); }
  public BStatusBoolean getValue() { return (BStatusBoolean)get("value"); }

////////////////////////////////////////////////////////////////
// Setters
////////////////////////////////////////////////////////////////

  public void setScheduleOrd(javax.baja.naming.BOrd v) { set("scheduleOrd", v); }
  public void setRanges(String v) { setString("ranges", v); }
  public void setClearFirst(boolean v) { setBoolean("clearFirst", v); }
  public void setApplySunday(boolean v) { setBoolean("applySunday", v); }
  public void setApplyMonday(boolean v) { setBoolean("applyMonday", v); }
  public void setApplyTuesday(boolean v) { setBoolean("applyTuesday", v); }
  public void setApplyWednesday(boolean v) { setBoolean("applyWednesday", v); }
  public void setApplyThursday(boolean v) { setBoolean("applyThursday", v); }
  public void setApplyFriday(boolean v) { setBoolean("applyFriday", v); }
  public void setApplySaturday(boolean v) { setBoolean("applySaturday", v); }
  public void setValue(javax.baja.status.BStatusBoolean v) { set("value", v); }

The biggest pain in the ass of this was the effectiveValue slot if the time slots that the Day inherits when entries are made. The effectiveValue slot has to be added programmatically otherwise, your start time and finish time will be entered but nothing is shown on the schedule interface at all, so it won’t see that it has any entries despite having entries.

This will have to be modified for allow for Numeric and Enum Schedules as well.

Thank you Charles.

Truly you are fantastic. If you’re ever in North West England I owe you a drink!

I’ve hopefully got a free afternoon tomorrow to try that out

1 Like

You’re more than welcome! This is just a crude one and more can be done with it but this will get you a time range, then choose the days you want to apply it to, then what you want that event to be (occupied/unoccupied, true/false, etc).

1 Like

Update: Used Charles’ code and the guidelines set by Ben Bartling’s GitHub, Agents.md to rewrite the code and first added 7 extra slots for individual day timings to the schedule. I then used it to add the code from Ben’s ICal programobject. With a bit more programming and some debugging help from Gemini. I now have a working programObject that can be linked to an outlook shared *.ics calendar and will dynamically change the BooleanSchedule. It takes the next 7 days and puts them into each slot, then pushes into the schedule.

Thanks Charles!

Thanks Ben!

2 Likes