001package jmri.jmris.srcp; 002 003import java.io.IOException; 004import java.io.OutputStream; 005 006import jmri.JmriException; 007import jmri.TimebaseRateException; 008import jmri.jmris.AbstractTimeServer; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012/** 013 * interface between the JMRI (fast) clock and an SRCP network connection 014 * 015 * @author Paul Bender Copyright (C) 2013 016 */ 017public class JmriSRCPTimeServer extends AbstractTimeServer { 018 019 private final OutputStream output; 020 021 private int modelrate = 1; 022 private int realrate = 1; 023 024 public JmriSRCPTimeServer(OutputStream outStream) { 025 super(); 026 output = outStream; 027 } 028 029 /* 030 * Protocol Specific Abstract Functions 031 */ 032 @Override 033 public void sendTime() throws IOException { 034 // prepare to format the date as <JulDay> <Hour> <Minute> <Seconds> 035 java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("HH mm ss"); 036 java.util.GregorianCalendar cal = new java.util.GregorianCalendar(); 037 cal.setTime(timebase.getTime()); 038 long day = jmri.util.DateUtil.julianDayFromCalendar(cal); 039 output.write(("100 INFO 0 TIME " + day + " " + sdf.format(timebase.getTime()) + "\n\r").getBytes()); 040 } 041 042 @Override 043 public void sendRate() throws IOException { 044 output.write(("101 INFO 0 TIME " + modelrate + " " + realrate + "\n\r").getBytes()); 045 } 046 047 @Override 048 public void sendStatus() throws IOException { 049 // send "102 INFO 0 TIME" if fastclock stops? 050 // sendRate() if fastclock starts? 051 } 052 053 @Override 054 public void sendErrorStatus() throws IOException { 055 } 056 057 @Override 058 public void parseTime(String statusString) throws JmriException, IOException { 059 // parsing is handled by the SRCP parser; this routine should never 060 // be called for SRCP 061 } 062 063 public void parseTime(long JulDay, int Hour, int Minute, int Second) { 064 java.util.GregorianCalendar cal = jmri.util.DateUtil.calFromJulianDate(JulDay); 065 cal.set(java.util.Calendar.HOUR, Hour); 066 cal.set(java.util.Calendar.MINUTE, Minute); 067 cal.set(java.util.Calendar.SECOND, Second); 068 timebase.userSetTime(cal.getTime()); 069 } 070 071 @Override 072 public void parseRate(String statusString) throws JmriException, IOException { 073 // parsing is handled by the SRCP parser; this routine should never 074 // be called for SRCP 075 } 076 077 public void parseRate(int modelRate, int realRate) { 078 modelrate = modelRate; 079 realrate = realRate; 080 try { 081 timebase.userSetRate((double) modelRate / (double) realRate); 082 } catch (TimebaseRateException ex) { 083 // Although this Exception is declared to be thrown for userSetRate, 084 // it is never created in code, so just log it should we begin to see it 085 log.error("Something went wrong", ex); 086 } 087 } 088 089 @Override 090 public void stopTime() { 091 super.stopTime(); 092 // when the clock stops, we need to notify any 093 // waiting timers the clock has stoped. 094 } 095 096 public void setAlarm(long JulDay, int Hour, int Minute, int Second) { 097 if (log.isDebugEnabled()) { 098 log.debug("setting alarm for {} {}:{}:{}", JulDay, Hour, Minute, Second); 099 } 100 101 java.util.GregorianCalendar cal = jmri.util.DateUtil.calFromJulianDate(JulDay); 102 cal.set(java.util.Calendar.HOUR, Hour); 103 cal.set(java.util.Calendar.MINUTE, Minute); 104 cal.set(java.util.Calendar.SECOND, Second); 105 106 java.util.GregorianCalendar now = new java.util.GregorianCalendar(); 107 now.setTime(timebase.getTime()); 108 if (now.after(cal)) { 109 try { 110 sendTime(); 111 } catch (IOException ex) { 112 log.warn("Unable to send message to client: {}", ex.getMessage()); 113 } 114 } else { 115 // add this alarm to the list of alarms. 116 if (alarmList == null) { 117 alarmList = new java.util.ArrayList<>(); 118 } 119 alarmList.add(cal); 120 // and start the timeListener. 121 listenToTimebase(true); 122 try { 123 output.write("200 Ok\n\r".getBytes()); 124 } catch (IOException ie) { 125 log.warn("Unable to send message to client: {}", ie.getMessage()); 126 } 127 } 128 } 129 130 private java.util.ArrayList<java.util.GregorianCalendar> alarmList = null; 131 132 private void checkAlarmList() throws IOException { 133 if (alarmList == null) { 134 return; 135 } 136 137 java.util.GregorianCalendar cal = new java.util.GregorianCalendar(); 138 cal.setTime(timebase.getTime()); 139 if (log.isDebugEnabled()) { 140 log.debug("checking alarms at {} {}:{}:{}", jmri.util.DateUtil.julianDayFromCalendar(cal), cal.get(java.util.Calendar.HOUR_OF_DAY), cal.get(java.util.Calendar.MINUTE), cal.get(java.util.Calendar.SECOND)); 141 } 142 java.util.Iterator<java.util.GregorianCalendar> alarm = alarmList.iterator(); 143 while (alarm.hasNext()) { 144 if (cal.after(alarm.next())) { 145 sendTime(); 146 alarm.remove(); 147 } 148 } 149 } 150 151 @Override 152 public void listenToTimebase(boolean listen) { 153 if (!listen && timeListener == null) { 154 return; // nothing to do. 155 } 156 if (timeListener == null) { 157 timeListener = evt -> { 158 try { 159 if (evt.getPropertyName().equals("minutes")) { 160 checkAlarmList(); 161 } 162 } catch (IOException ex) { 163 log.warn("Unable to send message to client: {}", ex.getMessage()); 164 timebase.removeMinuteChangeListener(timeListener); 165 } 166 }; 167 } 168 if (listen) { 169 timebase.addMinuteChangeListener(timeListener); 170 } else { 171 timebase.removeMinuteChangeListener(timeListener); 172 } 173 } 174 175 private static final Logger log = LoggerFactory.getLogger(JmriSRCPTimeServer.class); 176 177}