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 - 2019 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    @SuppressWarnings("unchecked")
204    <T extends W1Device> List<T> readDevices() {
205        final List<W1Device> devices = new ArrayList<>();
206        for (final File deviceDir : getDeviceDirs()) {
207            final String id = deviceDir.getName().substring(0, 2).toUpperCase();
208            final W1DeviceType w1DeviceType = deviceTypeMap.get(id);
209            if (w1DeviceType != null) {
210                final W1Device w1Device = w1DeviceType.create(deviceDir);
211                devices.add(w1Device);
212            } else {
213                log.info("no device type for [" + id + "] found - ignoring");
214            }
215        }
216        return (List<T>) devices;
217    }
218
219    /*
220    public <T extends W1Device> List<T> getDevices(final String deviceFamilyId) {
221        List<W1Device> devices = new ArrayList<>();
222        for (W1Device device : readDevices()) {
223
224            if (deviceFamilyId == null || deviceFamilyId.toUpperCase().equals(device.getId())) {
225                devices.add(device);
226            }
227        }
228        return (List<T>) devices;
229    }
230     */
231
232    /**
233     * Get a list of devices that implement a certain interface.
234     *
235     * @param type
236     * @param <T>
237     * @return
238     */
239    @SuppressWarnings("unchecked")
240    public <T> List<T> getDevices(final Class<T> type) {
241        final List<W1Device> allDevices = getDevices();
242        final List<T> filteredDevices = new ArrayList<>();
243        for (final W1Device device : allDevices) {
244            if (type.isAssignableFrom(device.getClass())) {
245                filteredDevices.add((T) device);
246            }
247        }
248        return filteredDevices;
249    }
250
251    @SuppressWarnings("unchecked")
252    public <T extends W1Device> List<T> getW1Devices(final Class<T> type) {
253        for (final W1DeviceType deviceType : deviceTypes) {
254            if (deviceType.getDeviceClass().equals(type)) {
255                return (List<T>) getDevices(deviceType.getDeviceFamilyCode());
256            }
257        }
258        return Collections.emptyList();
259    }
260
261    @Override
262    public String toString() {
263        final StringBuilder builder = new StringBuilder();
264        builder.append("W1 Master: ").append(masterDir).append("\n");
265        builder.append("Device Types: \n");
266        for (final W1DeviceType deviceType : deviceTypeMap.values()) {
267            builder.append(" - ").append(Integer.toHexString(deviceType.getDeviceFamilyCode()));
268            builder.append(" = ").append(deviceType.getDeviceClass());
269            builder.append("\n");
270        }
271        builder.append("Devices:\n");
272        for (final W1Device device : getDevices()) {
273            builder.append(" - ").append(device.getId()).append(": ").append(device.getName());
274            builder.append(" = ").append(device.getClass().getName()).append("\n");
275        }
276        return builder.toString();
277    }
278}