001package silicondust.ant.taskdefs; 002/*- 003 * ########################################################################## 004 * TV H/W, EPGs, and Recording 005 * $Id: HDHRTunerTask.java 5999 2020-05-18 16:41:28Z ball $ 006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/silicondust/trunk/src/main/java/silicondust/ant/taskdefs/HDHRTunerTask.java $ 007 * %% 008 * Copyright (C) 2013 - 2020 Allen D. Ball 009 * %% 010 * Licensed under the Apache License, Version 2.0 (the "License"); 011 * you may not use this file except in compliance with the License. 012 * You may obtain a copy of the License at 013 * 014 * http://www.apache.org/licenses/LICENSE-2.0 015 * 016 * Unless required by applicable law or agreed to in writing, software 017 * distributed under the License is distributed on an "AS IS" BASIS, 018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 019 * See the License for the specific language governing permissions and 020 * limitations under the License. 021 * ########################################################################## 022 */ 023import ball.swing.table.ArrayListTableModel; 024import ball.swing.table.SimpleTableModel; 025import ball.util.ant.taskdefs.AnnotatedAntTask; 026import ball.util.ant.taskdefs.AntTask; 027import ball.util.ant.taskdefs.ClasspathDelegateAntTask; 028import ball.util.ant.taskdefs.ConfigurableAntTask; 029import java.net.InetAddress; 030import java.util.ArrayList; 031import java.util.List; 032import java.util.Map; 033import java.util.SortedMap; 034import lombok.Getter; 035import lombok.NoArgsConstructor; 036import lombok.Setter; 037import lombok.ToString; 038import lombok.experimental.Accessors; 039import org.apache.tools.ant.BuildException; 040import org.apache.tools.ant.Project; 041import org.apache.tools.ant.Task; 042import org.apache.tools.ant.util.ClasspathUtils; 043import silicondust.Channel; 044import silicondust.ChannelRangeMap; 045import silicondust.HDHRProgram; 046import silicondust.HDHRTuner; 047 048import static java.util.Comparator.comparing; 049import static java.util.Objects.requireNonNull; 050import static java.util.function.Function.identity; 051import static java.util.stream.Collectors.counting; 052import static java.util.stream.Collectors.groupingBy; 053import static lombok.AccessLevel.PROTECTED; 054import static org.apache.commons.lang3.StringUtils.EMPTY; 055 056/** 057 * {@link.uri http://ant.apache.org/ Ant} {@link Task} for HDHomeRun tuners. 058 * 059 * {@ant.task} 060 * 061 * @see Options 062 * @see Scan 063 * 064 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 065 * @version $Revision: 5999 $ 066 */ 067@AntTask("hdhr-tuner") 068@NoArgsConstructor @ToString 069public class HDHRTunerTask extends Task implements AnnotatedAntTask, 070 ClasspathDelegateAntTask, 071 ConfigurableAntTask { 072 private static final String COLON = ":"; 073 private static final String DOT = "."; 074 private static final String EQUALS = "="; 075 076 @Getter @Setter @Accessors(chain = true, fluent = true) 077 private ClasspathUtils.Delegate delegate = null; 078 @Getter 079 private Integer id = null; 080 @Getter 081 private InetAddress address = null; 082 @Getter @Setter 083 private int key = 0; 084 private ArrayList<Directive> list = new ArrayList<>(); 085 protected transient HDHRTuner tuner = null; 086 087 public void setID(String id) throws BuildException { 088 try { 089 this.id = Integer.parseInt(id, 16); 090 } catch (BuildException exception) { 091 throw exception; 092 } catch (RuntimeException exception) { 093 throw exception; 094 } catch (Throwable throwable) { 095 throw new BuildException(throwable); 096 } 097 } 098 099 public void setAddress(String address) throws BuildException { 100 try { 101 this.address = InetAddress.getByName(address); 102 } catch (BuildException exception) { 103 throw exception; 104 } catch (RuntimeException exception) { 105 throw exception; 106 } catch (Throwable throwable) { 107 throw new BuildException(throwable); 108 } 109 } 110 111 public List<Directive> getDirectiveList() { return list; } 112 public void addConfiguredGet(Get get) { getDirectiveList().add(get); } 113 public void addConfiguredSet(Set set) { getDirectiveList().add(set); } 114 115 @Override 116 public void init() throws BuildException { 117 super.init(); 118 ClasspathDelegateAntTask.super.init(); 119 ConfigurableAntTask.super.init(); 120 } 121 122 @Override 123 public void execute() throws BuildException { 124 super.execute(); 125 AnnotatedAntTask.super.execute(); 126 127 if (getId() == null && getAddress() == null) { 128 throw new BuildException("Either `id' or `address' attribute must be specified"); 129 } 130 131 try { 132 if (getAddress() != null) { 133 tuner = new HDHRTuner(getAddress()); 134 } else { 135 tuner = HDHRTuner.discover().get(getId()); 136 } 137 138 requireNonNull(tuner, "tuner"); 139 140 SimpleTableModel model = 141 new SimpleTableModel(new Object[][] { }, 2); 142 143 for (Directive directive : getDirectiveList()) { 144 if (directive instanceof Set) { 145 Set set = (Set) directive; 146 147 tuner.set(getKey(), set.getName(), set.getValue()); 148 } 149 150 model.row(directive.getName(), 151 tuner.get(getKey(), directive.getName())); 152 } 153 154 log(model); 155 } catch (BuildException exception) { 156 throw exception; 157 } catch (Throwable throwable) { 158 String tuner = 159 (getAddress() != null) 160 ? getAddress().getHostAddress() 161 : Integer.toString(getId(), 16); 162 163 throw new BuildException("Cannot locate tuner " + tuner, 164 throwable); 165 } 166 } 167 168 @NoArgsConstructor(access = PROTECTED) 169 protected static abstract class Directive { 170 @Getter @Setter private String name = null; 171 172 @Override 173 public String toString() { return getName(); } 174 } 175 176 /** 177 * {@link HDHRTunerTask} get {@link Directive}. 178 */ 179 @NoArgsConstructor 180 public static class Get extends Directive { 181 } 182 183 /** 184 * {@link HDHRTunerTask} set {@link Directive}. 185 */ 186 @NoArgsConstructor 187 public static class Set extends Directive { 188 @Getter @Setter private String value = null; 189 190 @Override 191 public String toString() { return getName() + EQUALS + getValue(); } 192 } 193 194 /** 195 * {@link.uri http://ant.apache.org/ Ant} 196 * {@link org.apache.tools.ant.Task} to get all tag names from a 197 * HDHomeRun tuner. 198 * 199 * {@ant.task} 200 */ 201 @AntTask("hdhr-tuner-options") 202 @NoArgsConstructor @ToString 203 public static class Options extends HDHRTunerTask { 204 @Override 205 public void execute() throws BuildException { 206 super.execute(); 207 208 try { 209 log(tuner.options().stream()); 210 } catch (BuildException exception) { 211 throw exception; 212 } catch (Throwable throwable) { 213 throw new BuildException(throwable); 214 } 215 } 216 } 217 218 /** 219 * {@link.uri http://ant.apache.org/ Ant} 220 * {@link org.apache.tools.ant.Task} to scan a HDHomeRun tuner for 221 * channels. 222 * 223 * {@ant.task} 224 */ 225 @AntTask("hdhr-tuner-scan") 226 @NoArgsConstructor @ToString 227 public static class Scan extends HDHRTunerTask { 228 private static final String ENCRYPTED = "encrypted"; 229 230 private transient List<HDHRProgram> list = null; 231 private transient ChannelRangeMap rangeMap = null; 232 private transient SortedMap<Integer,Channel> frequencyMap = null; 233 234 @Override 235 public void execute() throws BuildException { 236 super.execute(); 237 238 try { 239 long timestamp = System.currentTimeMillis(); 240 241 list = tuner.scan(); 242 list.removeIf(t -> t.getFlags().contains(ENCRYPTED)); 243 244 rangeMap = 245 list.stream() 246 .flatMap(t -> Channel.MAP.get(t.getFrequency()).stream()) 247 .map(t -> t.getMapName()) 248 .collect(groupingBy(identity(), counting())) 249 .entrySet() 250 .stream() 251 .max(comparing(Map.Entry::getValue)) 252 .map(t -> ChannelRangeMap.forName(t.getKey())) 253 .orElse(null); 254 255 if (rangeMap != null) { 256 frequencyMap = rangeMap.frequencyMap(); 257 258 if (frequencyMap != null) { 259 list.removeIf(t -> (! frequencyMap.keySet().contains(t.getFrequency()))); 260 } 261 262 for (int i = 0, n = tuner.getCount(); i < n; i += 1) { 263 try { 264 tuner.channelmap(i, 0, rangeMap.name()); 265 } catch (Exception exception) { 266 log(exception, Project.MSG_WARN); 267 } 268 } 269 } 270 271 log(); 272 log(new TableModelImpl(list)); 273 } catch (BuildException exception) { 274 throw exception; 275 } catch (Throwable throwable) { 276 throw new BuildException(throwable); 277 } 278 } 279 280 private class TableModelImpl extends ArrayListTableModel<HDHRProgram> { 281 private static final long serialVersionUID = -8478080367042692419L; 282 283 public TableModelImpl(Iterable<HDHRProgram> iterable) { 284 super(iterable, 285 (rangeMap != null) ? rangeMap.name() : EMPTY, 286 "Frequency", "Program", EMPTY); 287 } 288 289 @Override 290 protected Object getValueAt(HDHRProgram row, int x) { 291 Object object = null; 292 293 switch (x) { 294 case 0: 295 default: 296 Channel channel = 297 (frequencyMap != null) 298 ? frequencyMap.get(row.getFrequency()) 299 : null; 300 301 if (channel != null) { 302 object = channel.getNumber(); 303 } 304 break; 305 306 case 1: object = row.getFrequency(); break; 307 case 2: object = row.getNumber(); break; 308 case 3: object = row.getName(); break; 309 } 310 311 if (object == null) { 312 object = EMPTY; 313 } 314 315 return object; 316 } 317 } 318 } 319}