View Javadoc
1   package net.sf.mbus4j.slaves;
2   
3   /*
4    * #%L
5    * mbus4j-slaves
6    * %%
7    * Copyright (C) 2009 - 2014 MBus4J
8    * %%
9    * mbus4j - Drivers for the M-Bus protocol - http://mbus4j.sourceforge.net/
10   * Copyright (C) 2009-2014, mbus4j.sf.net, and individual contributors as indicated
11   * by the @authors tag. See the copyright.txt in the distribution for a
12   * full listing of individual contributors.
13   * 
14   * This is free software; you can redistribute it and/or modify it
15   * under the terms of the GNU General Public License as
16   * published by the Free Software Foundation; either version 3 of
17   * the License, or (at your option) any later version.
18   * 
19   * This software is distributed in the hope that it will be useful,
20   * but WITHOUT ANY WARRANTY; without even the implied warranty of
21   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22   * Lesser General Public License for more details.
23   * 
24   * You should have received a copy of the GNU Lesser General Public
25   * License along with this software; if not, write to the Free
26   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
27   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
28   * #L%
29   */
30  
31  import net.sf.json.JSONArray;
32  import net.sf.json.JSONObject;
33  
34  import net.sf.mbus4j.MBusUtils;
35  import net.sf.mbus4j.dataframes.ApplicationReset;
36  import net.sf.mbus4j.dataframes.Frame;
37  import net.sf.mbus4j.dataframes.MBusMedium;
38  import net.sf.mbus4j.dataframes.MBusResponseFramesContainer;
39  import net.sf.mbus4j.dataframes.PrimaryAddress;
40  import net.sf.mbus4j.dataframes.RequestClassXData;
41  import net.sf.mbus4j.dataframes.ResponseFrameContainer;
42  import net.sf.mbus4j.dataframes.SelectionOfSlaves;
43  import net.sf.mbus4j.dataframes.SendInitSlave;
44  import net.sf.mbus4j.dataframes.SendUserData;
45  import net.sf.mbus4j.dataframes.SendUserDataManSpec;
46  import net.sf.mbus4j.dataframes.SingleCharFrame;
47  import net.sf.mbus4j.dataframes.UserDataResponse;
48  import net.sf.mbus4j.dataframes.UserDataResponse.StatusCode;
49  import net.sf.mbus4j.json.JSONFactory;
50  import net.sf.mbus4j.json.JSONSerializable;
51  import net.sf.mbus4j.json.JsonSerializeType;
52  
53  import java.io.IOException;
54  import java.io.Serializable;
55  import java.util.ArrayList;
56  import java.util.Iterator;
57  import java.util.List;
58  import java.util.logging.Level;
59  import java.util.logging.Logger;
60  import net.sf.mbus4j.log.LogUtils;
61  
62  /**
63   *
64   * @author arnep@users.sourceforge.net
65   * @version $Id: Slave.java 104 2014-02-21 09:31:17Z arnep $
66   */
67  public class Slave
68          implements Serializable,
69          MBusResponseFramesContainer,
70          JSONSerializable {
71  
72      private final static Logger log = LogUtils.getSlaveLogger();
73  
74      public static Slave fromResponse(UserDataResponse udr) {
75          Slave result = new Slave(udr.getAddress(), udr.getIdentNumber(), udr.getManufacturer(), udr.getVersion(), udr.getMedium());
76          result.setAccessnumber(udr.getAccessNumber());
77          result.setAcd(udr.isAcd());
78          result.setDfc(udr.isDfc());
79          result.setSignature(udr.getSignature());
80          result.setStatus(udr.getStatus());
81          ResponseFrameContainer rfc = new ResponseFrameContainer();
82          rfc.setRequestFrame(new RequestClassXData(Frame.ControlCode.REQ_UD2));
83          rfc.setResponseFrame(udr);
84          result.addResponseFrameContainer(rfc);
85          return result;
86      }
87  
88      /**
89       * Template for creating a valid udr - no datablocks.
90       */
91      private boolean networkSelected;
92      protected List<ResponseFrameContainer> responseFrameContainers = new ArrayList<ResponseFrameContainer>();
93      private StatusCode[] status;
94      private byte version;
95      private MBusMedium medium;
96      private byte address;
97      private int identNumber;
98      private String manufacturer;
99      private short signature;
100     private short accessNumber;
101     private boolean acd;
102     private boolean dfc;
103     private int selectedIndex;
104 
105     public Slave() {
106         super();
107     }
108 
109     public Slave(int primaryAddress, int id, String man, int version, MBusMedium medium) {
110         this();
111         networkSelected = false;
112         setAddress((byte) primaryAddress);
113         setIdentNumber(id);
114         setManufacturer(man);
115         setVersion((byte) version);
116         setMedium(medium);
117         setStatus(new UserDataResponse.StatusCode[]{UserDataResponse.StatusCode.APPLICATION_NO_ERROR});
118         selectedIndex = -1;
119     }
120 
121     @Override
122     public byte getAddress() {
123         return address;
124     }
125 
126     @Override
127     public int getIdentNumber() {
128         return identNumber;
129     }
130 
131     @Override
132     public String getManufacturer() {
133         return manufacturer;
134     }
135 
136     @Override
137     public MBusMedium getMedium() {
138         return medium;
139     }
140 
141     @Override
142     public byte getVersion() {
143         return version;
144     }
145 
146     @Override
147     public StatusCode[] getStatus() {
148         return status;
149     }
150 
151     @Override
152     public void setStatus(StatusCode[] status) {
153         this.status = status;
154     }
155 
156     @Override
157     public short getSignature() {
158         return signature;
159     }
160 
161     @Override
162     public void setSignature(short signature) {
163         this.signature = signature;
164     }
165 
166     public Frame handleApplicationReset(ApplicationReset applicationReset) {
167         for (int i = 0; i < responseFrameContainers.size(); i++) {
168             if (applicationReset.equals(responseFrameContainers.get(i).getSelectFrame())) {
169                 selectedIndex = i;
170                 log.log(Level.FINE, "Set selected response container to {0} \"{1}\"", new Object[]{i, getResponseFrameContainer(i).getName()});
171                 break;
172             }
173         }
174         return SingleCharFrame.SINGLE_CHAR_FRAME;
175     }
176 
177     public Frame handleReqUd1(RequestClassXData requestClassXData) {
178         throw new UnsupportedOperationException("Not yet implemented");
179     }
180 
181     public Frame handleReqUd2(RequestClassXData request) {
182         if (getSelectedResponseFrame() instanceof UserDataResponse) {
183             UserDataResponse udr = (UserDataResponse) getSelectedResponseFrame();
184             udr.setAccessNumber(accessNumber++);
185             udr.setAcd(acd);
186             udr.setAddress(address);
187             udr.setDfc(dfc);
188             udr.setIdentNumber(identNumber);
189             udr.setManufacturer(manufacturer);
190             udr.setMedium(medium);
191             udr.setSignature(signature);
192             udr.setStatus(status);
193             udr.setVersion(version);
194 
195             return udr;
196         } else if (getSelectedResponseFrame() == null) {
197             UserDataResponse udr = new UserDataResponse();
198             udr.setAccessNumber(accessNumber++);
199             udr.setAcd(acd);
200             udr.setAddress(address);
201             udr.setDfc(dfc);
202             udr.setIdentNumber(identNumber);
203             udr.setManufacturer(manufacturer);
204             udr.setMedium(medium);
205             udr.setSignature(signature);
206             udr.setStatus(status);
207             udr.setVersion(version);
208 
209             return udr;
210         } else {
211             throw new RuntimeException("Unknown Response class: "
212                     + getSelectedResponseFrame().getClass().getName());
213         }
214     }
215 
216     public Frame handleSendInitSlave(SendInitSlave sendInitSlave) {
217         return SingleCharFrame.SINGLE_CHAR_FRAME;
218     }
219 
220     public Frame handleSendUserData(SendUserData sendUserData) {
221         return SingleCharFrame.SINGLE_CHAR_FRAME;
222     }
223 
224     public Frame handleSendUserDataManSpec(SendUserDataManSpec sendUserDataManSpec) {
225         return SingleCharFrame.SINGLE_CHAR_FRAME;
226     }
227 
228     @Override
229     public void setAddress(byte address) {
230         this.address = address;
231     }
232 
233     @Override
234     public void setIdentNumber(int id) {
235         this.identNumber = id;
236     }
237 
238     @Override
239     public void setManufacturer(String man) {
240         this.manufacturer = man;
241     }
242 
243     @Override
244     public void setMedium(MBusMedium medium) {
245         this.medium = medium;
246     }
247 
248     @Override
249     public void setVersion(byte version) {
250         this.version = version;
251     }
252 
253     /**
254      *
255      * @param frame
256      * @return
257      */
258     public boolean willHandleRequest(Frame frame) {
259         if (frame instanceof SelectionOfSlaves) {
260             SelectionOfSlaves selectionOfSlaves = (SelectionOfSlaves) frame;
261             log.log(Level.INFO, "will handle SelectionOfSlaves: {0}", (selectionOfSlaves.getAddress() & 0xFF) == MBusUtils.SLAVE_SELECT_PRIMARY_ADDRESS);
262 
263             return (selectionOfSlaves.getAddress() & 0xFF) == MBusUtils.SLAVE_SELECT_PRIMARY_ADDRESS;
264         } else if (frame instanceof PrimaryAddress) {
265             int primaryAddress = ((PrimaryAddress) frame).getAddress() & 0xFF;
266 
267             return willHandleByAddress(primaryAddress);
268         } else if (frame instanceof RequestClassXData) {
269             int primaryAddress = ((RequestClassXData) frame).getAddress() & 0xFF;
270 
271             return willHandleByAddress(primaryAddress);
272         } else {
273             return false;
274         }
275     }
276 
277     Frame handleSelectionOfSlaves(SelectionOfSlaves selectionOfSlaves) {
278         if ((selectionOfSlaves.getAddress() & 0xFF) != MBusUtils.SLAVE_SELECT_PRIMARY_ADDRESS) {
279             log.warning("NETWORK SELECT ERROR");
280 
281             return null;
282         }
283 
284         if (selectionOfSlaves.matchAll(getIdentNumber(),
285                 getManufacturer(),
286                 getMedium(),
287                 getVersion())) {
288             networkSelected = true;
289             log.log(Level.INFO, "Network selected: {0}", slaveIdToString());
290 
291             return SingleCharFrame.SINGLE_CHAR_FRAME;
292         } else {
293             log.log(Level.INFO, "Network deselected: {0}", slaveIdToString());
294             networkSelected = false;
295 
296             return null;
297         }
298     }
299 
300     /**
301      * @return the networkSelected
302      */
303     public boolean isNetworkSelected() {
304         return networkSelected;
305     }
306 
307     /**
308      * @param networkSelected the networkSelected to set
309      */
310     public void setNetworkSelected(boolean networkSelected) {
311         this.networkSelected = networkSelected;
312     }
313 
314     public String slaveIdToString() {
315         return String.format("address = 0x%02x, id = %08d, man = %s, medium = %s, version = 0x%02X",
316                 getAddress(),
317                 getIdentNumber(),
318                 getManufacturer(),
319                 getMedium(),
320                 getVersion());
321     }
322 
323     private boolean willHandleByAddress(int primaryAddress) {
324         return (primaryAddress == MBusUtils.BROADCAST_NO_ANSWER_PRIMARY_ADDRESS)
325                 || (primaryAddress == MBusUtils.BROADCAST_WITH_ANSWER_PRIMARY_ADDRESS)
326                 || (primaryAddress == (getAddress() & 0xFF))
327                 || ((primaryAddress == MBusUtils.SLAVE_SELECT_PRIMARY_ADDRESS) && isNetworkSelected());
328     }
329 
330     @Override
331     public boolean equals(Object o) {
332         if (o instanceof Slave) {
333             Slave other = (Slave) o;
334 
335             return (getAddress() == other.getAddress()) && (getIdentNumber() == other.getIdentNumber())
336                     && getManufacturer().equals(other.getManufacturer())
337                     && getMedium().equals(other.getMedium()) && (getVersion() == other.getVersion());
338         } else {
339             return false;
340         }
341     }
342 
343     @Override
344     public int hashCode() {
345         int hash = 7;
346         hash = 79 * hash + this.version;
347         hash = 79 * hash + (this.medium != null ? this.medium.hashCode() : 0);
348         hash = 79 * hash + this.address;
349         hash = 79 * hash + this.identNumber;
350         hash = 79 * hash + (this.manufacturer != null ? this.manufacturer.hashCode() : 0);
351         return hash;
352     }
353 
354     /**
355      * track the version of self saved data !!!
356      */
357     private static final long serialVersionUID = -1;
358     private final static int SERIAL_VERSION = 1;
359 
360     private void writeObject(java.io.ObjectOutputStream stream)
361             throws IOException {
362         stream.defaultWriteObject();
363         stream.writeInt(SERIAL_VERSION);
364         stream.writeByte(getAddress());
365         stream.writeInt(getIdentNumber());
366         stream.writeObject(getManufacturer());
367         stream.writeObject(getMedium());
368         stream.writeByte(getVersion());
369         stream.writeBoolean(isAcd());
370         stream.writeBoolean(isDfc());
371     }
372 
373     private void readObject(java.io.ObjectInputStream stream)
374             throws IOException, ClassNotFoundException {
375         stream.defaultReadObject();
376 
377         switch (stream.readInt()) {
378             case 1:
379                 setAddress(stream.readByte());
380                 setIdentNumber(stream.readInt());
381                 setManufacturer((String) stream.readObject());
382                 setMedium((MBusMedium) stream.readObject());
383                 setVersion(stream.readByte());
384                 setAcd(stream.readBoolean());
385                 setDfc(stream.readBoolean());
386 
387                 break;
388 
389             default:
390         }
391     }
392 
393     @Override
394     public boolean isAcd() {
395         return acd;
396     }
397 
398     @Override
399     public boolean isDfc() {
400         return dfc;
401     }
402 
403     @Override
404     public void setAcd(boolean acd) {
405         this.acd = acd;
406     }
407 
408     @Override
409     public void setDfc(boolean dfc) {
410         this.dfc = dfc;
411     }
412 
413     @Override
414     public JSONObject toJSON(JsonSerializeType jsonSerializeType) {
415         JSONObject result = new JSONObject();
416         result.accumulate("manufacturer",
417                 getManufacturer());
418         result.accumulate("medium",
419                 getMedium().getLabel());
420         result.accumulate("version",
421                 JSONFactory.encodeHexByte(getVersion()));
422         result.accumulate("address",
423                 JSONFactory.encodeHexByte(getAddress()));
424         result.accumulate("signature",
425                 JSONFactory.encodeHexShort(getSignature()));
426 
427         JSONArray jsonStatusArray = new JSONArray();
428 
429         for (UserDataResponse.StatusCode st : getStatus()) {
430             jsonStatusArray.add(st.getLabel());
431         }
432 
433         result.accumulate("accessnumber",
434                 getAccessnumber());
435         result.accumulate("status", jsonStatusArray);
436         result.accumulate("identNumber",
437                 getIdentNumber());
438         result.accumulate("acd",
439                 isAcd());
440         result.accumulate("dfc",
441                 isDfc());
442         result.accumulate("selectedFrame", selectedIndex);
443 
444         JSONArray jsonSlaveFrameContainer = new JSONArray();
445 
446         for (ResponseFrameContainer sfc : responseFrameContainers) {
447             jsonSlaveFrameContainer.add(sfc.toJSON(jsonSerializeType));
448         }
449 
450         result.accumulate("slaveFrameContainers", jsonSlaveFrameContainer);
451 
452         return result;
453     }
454 
455     @Override
456     public Iterator<ResponseFrameContainer> iterator() {
457         return responseFrameContainers.iterator();
458     }
459 
460     @Override
461     public short getAccessnumber() {
462         return accessNumber;
463     }
464 
465     @Override
466     public void setAccessnumber(short accessnumber) {
467         this.accessNumber = accessnumber;
468     }
469 
470     @Override
471     public void fromJSON(JSONObject json) {
472         setManufacturer(json.getString("manufacturer"));
473         setMedium(MBusMedium.fromLabel(json.getString("medium")));
474         setVersion(JSONFactory.decodeHexByte(json, "version", (byte) 0));
475         setIdentNumber(json.getInt("identNumber"));
476         setAddress(JSONFactory.decodeHexByte(json, "address", (byte) 0));
477         setAccessnumber(JSONFactory.getShort(json, "accessNumber", (short) 0));
478         setAcd(JSONFactory.getBoolean(json, "acd", false));
479         setDfc(JSONFactory.getBoolean(json, "dfc", false));
480         setSignature(JSONFactory.decodeHexShort(json, "signature", (short) 0));
481 
482         if (json.containsKey("status")) {
483             JSONArray statusArray = json.getJSONArray("status");
484 
485             if (statusArray.size() == 0) {
486                 setStatus(new UserDataResponse.StatusCode[0]);
487             } else {
488                 UserDataResponse.StatusCode[] status = new UserDataResponse.StatusCode[statusArray.size()];
489 
490                 for (int i = 0; i < status.length; i++) {
491                     status[i] = UserDataResponse.StatusCode.fromLabel(statusArray.getString(i));
492                 }
493 
494                 setStatus(status);
495             }
496         }
497 
498         JSONArray jsonSlaveFrameContainers = json.getJSONArray("slaveFrameContainers");
499 
500         for (int i = 0; i < jsonSlaveFrameContainers.size(); i++) {
501             ResponseFrameContainer sfc = new ResponseFrameContainer();
502             sfc.fromJSON(jsonSlaveFrameContainers.getJSONObject(i));
503             responseFrameContainers.add(sfc);
504         }
505         if (json.containsKey("selectedFrame")) {
506             selectedIndex = json.getInt("selectedFrame");
507         } else {
508             selectedIndex = 0;
509         }
510     }
511 
512     @Override
513     public ResponseFrameContainer getResponseFrameContainer(int index) {
514         return responseFrameContainers.get(index);
515     }
516 
517     @Override
518     public ResponseFrameContainer[] getResponseFrameContainers() {
519         return responseFrameContainers.toArray(new ResponseFrameContainer[responseFrameContainers.size()]);
520     }
521 
522     @Override
523     public int getResponseFrameContainerCount() {
524         return responseFrameContainers.size();
525     }
526 
527     private Frame getSelectedResponseFrame() {
528         if (selectedIndex >= 0) {
529             return getResponseFrameContainer(selectedIndex).getResponseFrame();
530         } else {
531             return null;
532         }
533     }
534 
535     @Override
536     public boolean addResponseFrameContainer(ResponseFrameContainer rfc) {
537         return responseFrameContainers.add(rfc);
538     }
539 
540     @Override
541     public ResponseFrameContainer removeResponseFrameContainer(int i) {
542         return responseFrameContainers.remove(i);
543     }
544 
545     @Override
546     public int responseFrameContainerIndexOf(ResponseFrameContainer rfc) {
547         return responseFrameContainers.indexOf(rfc);
548     }
549 }