001package com.pi4j.io.w1; 002 003import java.io.File; 004import java.io.FilenameFilter; 005import java.util.ArrayList; 006import java.util.Arrays; 007import java.util.Collection; 008import java.util.Collections; 009import java.util.Iterator; 010import java.util.LinkedHashMap; 011import java.util.List; 012import java.util.Map; 013import java.util.ServiceLoader; 014import java.util.concurrent.CopyOnWriteArrayList; 015import java.util.logging.Logger; 016 017/* 018 * #%L 019 * ********************************************************************** 020 * ORGANIZATION : Pi4J 021 * PROJECT : Pi4J :: Java Library (Core) 022 * FILENAME : W1Master.java 023 * 024 * This file is part of the Pi4J project. More information about 025 * this project can be found here: https://www.pi4j.com/ 026 * ********************************************************************** 027 * %% 028 * Copyright (C) 2012 - 2021 Pi4J 029 * %% 030 * This program is free software: you can redistribute it and/or modify 031 * it under the terms of the GNU Lesser General Public License as 032 * published by the Free Software Foundation, either version 3 of the 033 * License, or (at your option) any later version. 034 * 035 * This program is distributed in the hope that it will be useful, 036 * but WITHOUT ANY WARRANTY; without even the implied warranty of 037 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 038 * GNU General Lesser Public License for more details. 039 * 040 * You should have received a copy of the GNU General Lesser Public 041 * License along with this program. If not, see 042 * <http://www.gnu.org/licenses/lgpl-3.0.html>. 043 * #L% 044 */ 045 046/** 047 * @author Peter Schuebl 048 */ 049public class W1Master { 050 051 private final Logger log = Logger.getLogger(W1Master.class.getName()); 052 053 private final List<W1DeviceType> deviceTypes = new ArrayList<>(); 054 055 private final Map<String, W1DeviceType> deviceTypeMap = new LinkedHashMap<>(); 056 057 private File masterDir = new File("/sys/bus/w1/devices"); 058 059 private final List<W1Device> devices = new CopyOnWriteArrayList<>(); 060 061 /** 062 * Create an instance of the W1 master. Typically there should only be one master. 063 * <p/> 064 * java.util.ServiceLoader is used to add device support for individual devices. 065 */ 066 public W1Master() { 067 init(null); 068 } 069 070 /** 071 * Create an instance of the W1 master. Typically there should only be one master. 072 * <p/> 073 * java.util.ServiceLoader is used to add device support for individual devices. 074 * 075 * @param classloader This ClassLoader will be used for the ServiceLoader to determine supported device types. 076 */ 077 public W1Master(final ClassLoader classloader) { 078 init(classloader); 079 } 080 081 /** 082 * Create a master with a different default dir e.g. for tests. 083 * 084 * @param masterDir 085 */ 086 public W1Master(final String masterDir) { 087 this.masterDir = new File(masterDir); 088 init(null); 089 } 090 091 /** 092 * Create a master with a different default dir e.g. for tests. 093 * 094 * @param masterDir 095 * @param classloader This ClassLoader will be used for the ServiceLoader to determine supported device types. 096 */ 097 public W1Master(final String masterDir, final ClassLoader classloader) { 098 this.masterDir = new File(masterDir); 099 init(classloader); 100 } 101 102 private void init(final ClassLoader classloader) { 103 final ServiceLoader<W1DeviceType> w1DeviceTypes = classloader == null ? ServiceLoader.load(W1DeviceType.class) : ServiceLoader.load(W1DeviceType.class, classloader); 104 final Iterator<W1DeviceType> w1DeviceTypeIterator = w1DeviceTypes.iterator(); 105 while (w1DeviceTypeIterator.hasNext()) { 106 final W1DeviceType w1DeviceType = w1DeviceTypeIterator.next(); 107 deviceTypes.add(w1DeviceType); 108 final String deviceFamily = Integer.toHexString(w1DeviceType.getDeviceFamilyCode()).toUpperCase(); 109 deviceTypeMap.put(deviceFamily, w1DeviceType); 110 } 111 devices.addAll(readDevices()); 112 } 113 114 public void checkDeviceChanges() { 115 final List<W1Device> refreshedDevices = new ArrayList<>(); 116 final List<W1Device> removedDevices = new ArrayList<>(); 117 118 refreshedDevices.addAll(readDevices()); 119 120 for (final W1Device device : devices) { 121 if (!refreshedDevices.contains(device)) { 122 removedDevices.add(device); 123 } 124 } 125 refreshedDevices.removeAll(devices); 126 127 final int newCount = refreshedDevices.size(); 128 final int removedCount = removedDevices.size(); 129 if (newCount > 0) { 130 log.fine("found " + newCount + " new device(s): " + refreshedDevices); 131 } 132 133 if (removedCount > 0) { 134 log.fine("removed " + removedCount + " device(s): " + removedDevices); 135 } 136 137 devices.addAll(refreshedDevices); 138 devices.removeAll(removedDevices); 139 } 140 141 /** 142 * Gets a list of the available device types. 143 * 144 * @return 145 */ 146 public Collection<W1DeviceType> getDeviceTypes() { 147 return deviceTypes; 148 } 149 150 public Map<String, W1DeviceType> getDeviceTypeMap() { 151 return deviceTypeMap; 152 } 153 154 private List<File> getDeviceDirs() { 155 final File[] slaveDevices = masterDir.listFiles(new FilenameFilter() { 156 @Override 157 public boolean accept(final File dir, final String name) { 158 return !name.contains("w1_bus_master"); 159 } 160 }); 161 if (slaveDevices != null) { 162 return Arrays.asList(slaveDevices); 163 } 164 return Collections.emptyList(); 165 } 166 167 /** 168 * Gets a list of all registered slave device ids. 169 * 170 * @return list of slave ids, can be empty, never null. 171 */ 172 public List<String> getDeviceIDs() { 173 final List<String> ids = new ArrayList<>(); 174 for (final File deviceDir : getDeviceDirs()) { 175 ids.add(deviceDir.getName()); 176 } 177 /* 178 * //for (final W1Device device: devices) { ids.add(device.getId()); 179 */ 180 return ids; 181 } 182 183 /** 184 * Get the list of available devices. 185 * 186 * @return returns an unmodifiable list of W1Devices. 187 */ 188 public List<W1Device> getDevices() { 189 return Collections.unmodifiableList(devices); 190 } 191 192 @SuppressWarnings("unchecked") 193 public <T extends W1Device> List<T> getDevices(final int deviceFamilyId) { 194 final List<W1Device> filteredDevices = new ArrayList<>(); 195 for (final W1Device device : devices) { 196 if (deviceFamilyId == device.getFamilyId()) { 197 filteredDevices.add(device); 198 } 199 } 200 return (List<T>) filteredDevices; 201 } 202 203 /** 204 * Get a single device by it's ID string 205 * @param id (string) 206 * @return W1 device instance 207 */ 208 @SuppressWarnings("unchecked") 209 public <T extends W1Device> T getDeviceById(final String id) { 210 final List<W1Device> filteredDevices = new ArrayList<>(); 211 for (final W1Device device : devices) { 212 if (device.getId().equalsIgnoreCase(id)) { 213 return (T)device; 214 } 215 } 216 return null; 217 } 218 219 @SuppressWarnings("unchecked") 220 <T extends W1Device> List<T> readDevices() { 221 final List<W1Device> devices = new ArrayList<>(); 222 for (final File deviceDir : getDeviceDirs()) { 223 final String id = deviceDir.getName().substring(0, 2).toUpperCase(); 224 final W1DeviceType w1DeviceType = deviceTypeMap.get(id); 225 if (w1DeviceType != null) { 226 final W1Device w1Device = w1DeviceType.create(deviceDir); 227 devices.add(w1Device); 228 } else { 229 log.info("no device type for [" + id + "] found - ignoring"); 230 } 231 } 232 return (List<T>) devices; 233 } 234 235 /* 236 public <T extends W1Device> List<T> getDevices(final String deviceFamilyId) { 237 List<W1Device> devices = new ArrayList<>(); 238 for (W1Device device : readDevices()) { 239 240 if (deviceFamilyId == null || deviceFamilyId.toUpperCase().equals(device.getId())) { 241 devices.add(device); 242 } 243 } 244 return (List<T>) devices; 245 } 246 */ 247 248 /** 249 * Get a list of devices that implement a certain interface. 250 * 251 * @param type 252 * @param <T> 253 * @return 254 */ 255 @SuppressWarnings("unchecked") 256 public <T> List<T> getDevices(final Class<T> type) { 257 final List<W1Device> allDevices = getDevices(); 258 final List<T> filteredDevices = new ArrayList<>(); 259 for (final W1Device device : allDevices) { 260 if (type.isAssignableFrom(device.getClass())) { 261 filteredDevices.add((T) device); 262 } 263 } 264 return filteredDevices; 265 } 266 267 @SuppressWarnings("unchecked") 268 public <T extends W1Device> List<T> getW1Devices(final Class<T> type) { 269 for (final W1DeviceType deviceType : deviceTypes) { 270 if (deviceType.getDeviceClass().equals(type)) { 271 return (List<T>) getDevices(deviceType.getDeviceFamilyCode()); 272 } 273 } 274 return Collections.emptyList(); 275 } 276 277 @Override 278 public String toString() { 279 final StringBuilder builder = new StringBuilder(); 280 builder.append("W1 Master: ").append(masterDir).append("\n"); 281 builder.append("Device Types: \n"); 282 for (final W1DeviceType deviceType : deviceTypeMap.values()) { 283 builder.append(" - ").append(Integer.toHexString(deviceType.getDeviceFamilyCode())); 284 builder.append(" = ").append(deviceType.getDeviceClass()); 285 builder.append("\n"); 286 } 287 builder.append("Devices:\n"); 288 for (final W1Device device : getDevices()) { 289 builder.append(" - ").append(device.getId()).append(": ").append(device.getName()); 290 builder.append(" = ").append(device.getClass().getName()).append("\n"); 291 } 292 return builder.toString(); 293 } 294}