View Javadoc
1   package net.sf.mbus4j.decoder;
2   
3   /*
4    * #%L
5    * mbus4j-core
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  import java.util.Calendar;
31  import java.util.Date;
32  import net.sf.mbus4j.MBusUtils;
33  
34  /**
35   *
36   * @author arnep@users.sourceforge.net
37   * @version $Id: Stack.java 104 2014-02-21 09:31:17Z arnep $
38   */
39  public class Stack {
40  
41      private int stackPos;
42      private byte[] data;
43  
44      public void clear() {
45          data = null;
46          stackPos = -1;
47      }
48  
49      public void init(int i) {
50          data = new byte[i];
51          stackPos = 0;
52      }
53  
54      public boolean isFull() {
55          return data.length == stackPos;
56      }
57  
58      public boolean peekIsTimestampRes1() {
59          return (data[stackPos - 4] & 0x40) == 0x40;
60      }
61  
62      public boolean peekIsTimestampRes2() {
63          return (data[stackPos - 3] & 0x40) == 0x40;
64      }
65  
66      public boolean peekIsTimestampRes3() {
67          return (data[stackPos - 3] & 0x20) == 0x20;
68      }
69  
70      public boolean peekIsTimestampSummertime() {
71          return (data[stackPos - 3] & 0x80) == 0x80;
72      }
73  
74      public boolean peekIsTimestampValid() {
75          return (data[stackPos - 4] & 0x80) != 0x80;
76      }
77  
78      public byte popBcdByte() {
79          return (byte) popBcdLong(2);
80      }
81  
82      /* convert to String (LCD recomendation) */
83      public char toLcdDigit(int bcdDigit) {
84          switch (bcdDigit) {
85              case 0x00:
86                  return '0';
87              case 0x01:
88                  return '1';
89              case 0x02:
90                  return '2';
91              case 0x03:
92                  return '3';
93              case 0x04:
94                  return '4';
95              case 0x05:
96                  return '5';
97              case 0x06:
98                  return '6';
99              case 0x07:
100                 return '7';
101             case 0x08:
102                 return '8';
103             case 0x09:
104                 return '9';
105             case 0x0A:
106                 return 'A';
107             case 0x0B:
108                 return 'b';
109             case 0x0C:
110                 return 'C';
111             case 0x0D:
112                 return ' ';
113             case 0x0E:
114                 return 'E';
115             case 0x0F:
116                 return '-';
117             default:
118                 throw new RuntimeException("Should never ever happend!");
119         }
120     }
121 
122     //TODO Pop BCD Error???
123     public String peekBcdError(int digits) {
124         char[] result = new char[digits];
125         int resultPos = 0;
126         final int floorPos = stackPos - (digits / 2);
127         boolean isError = false;
128 
129         for (int i = stackPos - 1; i >= floorPos; i--) {
130             result[resultPos++] = toLcdDigit((data[i] >> 4) & 0x0F);
131             isError |= ((i == (stackPos - 1)) && ((data[i] & 0x0F) == 0x0F));
132             result[resultPos++] += toLcdDigit(data[i] & 0x0F);
133             isError |= ((data[i] & 0x0F) > 0x09);
134         }
135 
136         if (isError) {
137             return new String(result);
138         } else {
139             return null;
140         }
141     }
142 
143     public int popBcdInteger(int digits) {
144         return (int) popBcdLong(digits);
145     }
146 
147     public short popBcdShort(int digits) {
148         return (short) popBcdLong(digits);
149     }
150 
151     public long popBcdLong(int digits) {
152         long result = 0;
153         final int floorPos = stackPos - (digits / 2);
154         boolean isNegative = false;
155 
156         for (int i = stackPos - 1; i >= floorPos; i--) {
157             result *= 10;
158 
159             if ((i == (stackPos - 1)) && (((data[i] >> 4) & 0x0F) == 0x0F)) {
160                 isNegative = true;
161             } else {
162                 result += ((data[i] >> 4) & 0x0F);
163             }
164 
165             result *= 10;
166             result += (data[i] & 0x0F);
167         }
168 
169         stackPos -= (digits / 2);
170 
171         if (isNegative) {
172             return -result;
173         } else {
174             return result;
175         }
176     }
177 
178     public byte popByte() {
179         return data[--stackPos];
180     }
181 
182     public byte[] popBytes() {
183         stackPos = 0;
184 
185         return data;
186     }
187 
188     public Date popDate() {
189         int val = popShort();
190         Calendar cal = Calendar.getInstance();
191         cal.set(Calendar.MILLISECOND, 0);
192         cal.set(Calendar.SECOND, 0);
193         cal.set(Calendar.MINUTE, 0);
194         cal.set(Calendar.HOUR_OF_DAY, 0);
195         cal.set(Calendar.DAY_OF_MONTH, val & 0x1F);
196         cal.set(Calendar.YEAR, 2000 + ((val >> 5) & 0x07) + ((val >> 9) & 0x78));
197         cal.set(Calendar.MONTH, ((val >> 8) & 0x0F) - 1);
198 
199         return cal.getTime();
200     }
201 
202     public float popFloat() {
203         return Float.intBitsToFloat(popInteger());
204     }
205 
206     public int popInteger() {
207         return ((data[--stackPos] << 24) & 0xFF000000) | ((data[--stackPos] << 16) & 0x00FF0000)
208                 | ((data[--stackPos] << 8) & 0x0000FF00) | (data[--stackPos] & 0x000000FF);
209     }
210 
211     public int popInteger(int bytes) {
212         int result = 0;
213 
214         for (int i = stackPos - 1; i >= (stackPos - bytes); i--) {
215             result <<= 8;
216             result += (data[i] & 0xFF);
217         }
218 
219         stackPos -= bytes;
220 
221         return result;
222     }
223 
224     public long popLong() {
225         return popLong(8);
226     }
227 
228     public long popLong(int bytes) {
229         long result = 0;
230 
231         for (int i = stackPos - 1; i >= (stackPos - bytes); i--) {
232             result <<= 8;
233             result += (data[i] & 0xFF);
234         }
235 
236         stackPos -= bytes;
237 
238         return result;
239     }
240 
241     public String popMan() {
242         return MBusUtils.short2Man(popShort());
243     }
244 
245     public short popShort() {
246         return (short) (((data[--stackPos] << 8) & 0xFF00) | (data[--stackPos] & 0x00FF));
247     }
248 
249     public String popString() {
250         StringBuilder sb = new StringBuilder();
251 
252         for (byte b1 : data) {
253             sb.insert(0, (char) b1);
254         }
255 
256         stackPos = 0;
257 
258         return sb.toString();
259     }
260 
261     public Date popTimeStamp() {
262         int val = popInteger(4);
263         Calendar cal = Calendar.getInstance();
264 
265         cal.set(Calendar.MILLISECOND, 0);
266         cal.set(Calendar.SECOND, 0);
267         cal.set(Calendar.MINUTE, val & 0x3F);
268         cal.set(Calendar.HOUR_OF_DAY, (val >> 8) & 0x1F);
269         cal.set(Calendar.DAY_OF_MONTH, (val >> 16) & 0x1F);
270         cal.set(Calendar.MONTH, ((val >> 24) & 0x0F) - 1);
271         cal.set(Calendar.YEAR, 2000 + ((val >> 21) & 0x07) + ((val >> 25) & 0x78));
272 
273         return cal.getTime();
274     }
275 
276     public void push(byte b) {
277         data[stackPos++] = b;
278     }
279 }