| @@ -1006,6 +1006,8 @@ public abstract class Broadcast | |||||
| */ | */ | ||||
| protected void pause(String reason, PrintWriter out) | protected void pause(String reason, PrintWriter out) | ||||
| { | { | ||||
| if (state == BroadcastState.ACCEPTED || state.isFinal) return; | |||||
| // Sets state to PAUSING, which is monitored by Broadcast.Service threads. | // Sets state to PAUSING, which is monitored by Broadcast.Service threads. | ||||
| // EVentually, when all service activity ends, the state transitions to PAUSED | // EVentually, when all service activity ends, the state transitions to PAUSED | ||||
| StateChangeResult result = setState(BroadcastState.PAUSING, reason, null); | StateChangeResult result = setState(BroadcastState.PAUSING, reason, null); | ||||
| @@ -1025,6 +1027,7 @@ public abstract class Broadcast | |||||
| protected void resume(String reason, PrintWriter out) | protected void resume(String reason, PrintWriter out) | ||||
| { | { | ||||
| if (state == BroadcastState.ACCEPTED || state.isFinal) return; | |||||
| if (!withinOperatingHours()) | if (!withinOperatingHours()) | ||||
| { | { | ||||
| if (out != null) out.write("Cannot resume outside operating hours"); | if (out != null) out.write("Cannot resume outside operating hours"); | ||||
| @@ -1450,16 +1453,37 @@ public abstract class Broadcast | |||||
| for (String key : new String[] {DAILY_STOP_KEY, DAILY_START_KEY}) { | for (String key : new String[] {DAILY_STOP_KEY, DAILY_START_KEY}) { | ||||
| String value = (String)configuration.get(key); | String value = (String)configuration.get(key); | ||||
| if (value != null) { | if (value != null) { | ||||
| timeChanged = timeChanged || setOperatingHours(key, value); | |||||
| if (setOperatingHours(key, value)) { | |||||
| timeChanged = true; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| if (timeChanged) enforceOperationHours(); | if (timeChanged) enforceOperationHours(); | ||||
| } | } | ||||
| /** | |||||
| * YML: At this time, we only enforce pause action when a broadcast is | |||||
| * outside its operating hours. The current design is not satisfactory and needs | |||||
| * a better solution. | |||||
| * | |||||
| * We are not automatically resuming a paused broadcast because the difference | |||||
| * between intention of an operator-initiated pause and | |||||
| * that of a clock pause needs clarification in their operation paradigm. | |||||
| * Question is when or if we allow a operator-initiated pause be resumed | |||||
| * when someone changes the operating hours of a broadcast in such a way | |||||
| * that the broadcast is at once within its operasting hours. it may be | |||||
| * be counter to the intention of the original operator. | |||||
| * | |||||
| * On the other hand, if that places the broadcast outside it operating hours, | |||||
| * it is safer to immediately pause it. | |||||
| * | |||||
| * To add clarity, we may need to separate the PAUSE state into OPERATOR_PAUSE and CLOCK_PAUSE, | |||||
| * and similarly PAUING state. | |||||
| */ | |||||
| void enforceOperationHours() { | void enforceOperationHours() { | ||||
| if (state == BroadcastState.ABORTED) return; | if (state == BroadcastState.ABORTED) return; | ||||
| if (withinOperatingHours()) { | if (withinOperatingHours()) { | ||||
| resume("clock", null); | |||||
| // resume("clock", null); | |||||
| } else { | } else { | ||||
| pause("clock", null); | pause("clock", null); | ||||
| } | } | ||||
| @@ -1476,11 +1500,11 @@ public abstract class Broadcast | |||||
| if (timeOfDay == null) throw new RuntimeException(String.format("Invalid value for %s: %s", timeParam, value)); | if (timeOfDay == null) throw new RuntimeException(String.format("Invalid value for %s: %s", timeParam, value)); | ||||
| switch (timeParam) { | switch (timeParam) { | ||||
| case DAILY_STOP_KEY: | case DAILY_STOP_KEY: | ||||
| if (daily_stop == timeOfDay) return false; | |||||
| if (timeOfDay.equals(daily_stop)) return false; | |||||
| daily_stop = timeOfDay; | daily_stop = timeOfDay; | ||||
| return true; | return true; | ||||
| case DAILY_START_KEY: | case DAILY_START_KEY: | ||||
| if (daily_start == timeOfDay) return false; | |||||
| if (timeOfDay.equals(daily_start)) return false; | |||||
| daily_start = timeOfDay; | daily_start = timeOfDay; | ||||
| return true; | return true; | ||||
| default: | default: | ||||
| @@ -5,6 +5,8 @@ import java.io.FileInputStream; | |||||
| import java.io.FileNotFoundException; | import java.io.FileNotFoundException; | ||||
| import java.io.IOException; | import java.io.IOException; | ||||
| import java.io.PrintWriter; | import java.io.PrintWriter; | ||||
| import java.time.LocalTime; | |||||
| import java.time.format.DateTimeFormatter; | |||||
| import java.util.Enumeration; | import java.util.Enumeration; | ||||
| import java.util.HashMap; | import java.util.HashMap; | ||||
| import java.util.Iterator; | import java.util.Iterator; | ||||
| @@ -25,6 +27,7 @@ import org.apache.log4j.Logger; | |||||
| import org.apache.log4j.PropertyConfigurator; | import org.apache.log4j.PropertyConfigurator; | ||||
| import org.json.simple.JSONObject; | import org.json.simple.JSONObject; | ||||
| import org.json.simple.parser.JSONParser; | import org.json.simple.parser.JSONParser; | ||||
| import org.json.simple.parser.ParseException; | |||||
| import altk.comm.engine.Broadcast.BroadcastState; | import altk.comm.engine.Broadcast.BroadcastState; | ||||
| @@ -92,13 +95,25 @@ public abstract class CommEngine extends HttpServlet | |||||
| { | { | ||||
| while (!threadShouldStop) | while (!threadShouldStop) | ||||
| { | { | ||||
| String timeOfDay = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm")); | |||||
| // Check for pause | |||||
| for (Broadcast broadcast : broadcasts.values()) | for (Broadcast broadcast : broadcasts.values()) | ||||
| { | |||||
| broadcast.enforceOperationHours(); | |||||
| {; | |||||
| // Check for pause | |||||
| if (broadcast.daily_stop.length() > 0) | |||||
| { | |||||
| if (timeOfDay.equals(broadcast.daily_stop)) broadcast.pause("clock", null); | |||||
| } | |||||
| // Check for resume | |||||
| if (broadcast.daily_start.length() > 0) | |||||
| { | |||||
| if (timeOfDay.equals(broadcast.daily_start)) broadcast.resume("clock", null); | |||||
| } | |||||
| } | } | ||||
| long currentTime = System.currentTimeMillis(); | long currentTime = System.currentTimeMillis(); | ||||
| long sleepTime = 60000 - currentTime % 60000; | |||||
| long sleepTime = 60000 + 30000 - currentTime % 60000; | |||||
| if (sleepTime > 0) | if (sleepTime > 0) | ||||
| { | { | ||||
| try | try | ||||
| @@ -508,13 +523,13 @@ public abstract class CommEngine extends HttpServlet | |||||
| protected static String checkTimeOfDay(String timeOfDay) { | protected static String checkTimeOfDay(String timeOfDay) { | ||||
| timeOfDay = timeOfDay.trim(); | timeOfDay = timeOfDay.trim(); | ||||
| // pattern hh:mm | // pattern hh:mm | ||||
| Pattern pattern = Pattern.compile("^(\\d\\d):[0-5]\\d$"); | |||||
| Pattern pattern = Pattern.compile("^(\\d+):([0-5]\\d)$"); | |||||
| Matcher matcher = pattern.matcher(timeOfDay); | Matcher matcher = pattern.matcher(timeOfDay); | ||||
| if (!matcher.find()) return null; | if (!matcher.find()) return null; | ||||
| // Check hour in range | // Check hour in range | ||||
| String hh = matcher.group(1); | |||||
| String hh = matcher.group(1).trim(); | |||||
| if (Integer.parseInt(hh) > 23) return null; | if (Integer.parseInt(hh) > 23) return null; | ||||
| return timeOfDay; | |||||
| return (hh.length()==1? "0" + hh : hh) + ":" + matcher.group(2); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -532,12 +547,15 @@ public abstract class CommEngine extends HttpServlet | |||||
| JSONObject configuration = (JSONObject) parser.parse(jsonString); | JSONObject configuration = (JSONObject) parser.parse(jsonString); | ||||
| configure(configuration); | configure(configuration); | ||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| myLogger.error(e); | |||||
| out.write("Error - " + e.getMessage()); | |||||
| String errMsg = | |||||
| (e instanceof ParseException)? ("JSON error: " + e) : e.getMessage(); | |||||
| myLogger.error(errMsg, e); | |||||
| out.write("Error - " + errMsg); | |||||
| // restore current confiuration | // restore current confiuration | ||||
| try { | try { | ||||
| configure(origConfigJSON); | configure(origConfigJSON); | ||||
| } catch (Exception e1) { | } catch (Exception e1) { | ||||
| myLogger.error("Internal error in restoring original configuration: " + e1.getMessage(), e1); | |||||
| out.write("\nInternal error in restoring original configuration: " + e1.getMessage()); | out.write("\nInternal error in restoring original configuration: " + e1.getMessage()); | ||||
| } | } | ||||
| } | } | ||||