UartBus source documentation
UartbusTools.java
1 package eu.javaexperience.electronic.uartbus;
2 
3 import java.lang.reflect.Proxy;
4 import java.math.BigInteger;
5 import java.util.Arrays;
6 
9 import eu.javaexperience.log.JavaExperienceLoggingFacility;
10 import eu.javaexperience.log.LogLevel;
11 import eu.javaexperience.log.Loggable;
12 import eu.javaexperience.log.Logger;
13 import eu.javaexperience.log.LoggingTools;
14 import eu.javaexperience.text.StringTools;
15 
16 public class UartbusTools
17 {
18  protected static final Logger LOG = JavaExperienceLoggingFacility.getLogger(new Loggable("UartbusTools"));
19 
20  private UartbusTools() {}
21 
22  public static byte crc8(byte[] data)
23  {
24  return crc8(data, data.length);
25  }
26 
27  public static byte crc8(byte[] data, int length)
28  {
29  byte crc = 0;
30  byte v;
31  int i;
32 
33  for(i=0;i<length;++i)
34  {
35  v = (byte) ((data[i] ^ crc) & 0xff);
36  crc = 0;
37  if((v & 1) != 0)
38  crc ^= 0x5e;
39  if((v & 2) != 0)
40  crc ^= 0xbc;
41  if((v & 4) != 0)
42  crc ^= 0x61;
43  if((v & 8) != 0)
44  crc ^= 0xc2;
45  if((v & 0x10) != 0)
46  crc ^= 0x9d;
47  if((v & 0x20) != 0)
48  crc ^= 0x23;
49  if((v & 0x40) != 0)
50  crc ^= 0x46;
51  if((v & 0x80) != 0)
52  crc ^= 0x8c;
53  }
54 
55  return crc;
56  }
57 
58  public static byte[] parseColonData(String data)
59  {
60  String[] ns = StringTools.plainSplit(data, ":");
61  byte[] ret = new byte[ns.length];
62  for(int i=0;i<ns.length;++i)
63  {
64  ret[i] = (byte) (Integer.parseInt(ns[i]) & 0xff);
65  }
66  return ret;
67  }
68 
69  public static String formatColonData(byte[] e)
70  {
71  StringBuilder sb = new StringBuilder();
72  for(int i=0;i<e.length;++i)
73  {
74  if(i != 0)
75  {
76  sb.append(":");
77  }
78  sb.append(0xff & e[i]);
79  }
80  return sb.toString();
81  }
82 
83  public static String formatColonDataWithValidation(byte[] e)
84  {
85  if(UartbusTools.crc8(e, e.length-1) != e[e.length-1])
86  {
87  return "!"+formatColonData(e);
88  }
89  return formatColonData(e);
90  }
91 
92  public static enum PacketFormattingMode
93  {
94  RAW_COLON,
95  RAW_COLON_WITH_VERIFY,
96  PARSED_PACKET
97  }
98 
99  public static String formatPacketWithMode(PacketFormattingMode mode, byte[] data)
100  {
101  switch(mode)
102  {
103  case PARSED_PACKET:
104  try
105  {
106  return ParsedUartBusPacket.fromRawPacketWithCrc(data).toShortUserText();
107  }
108  catch(Throwable t)
109  {
110  return formatColonDataWithValidation(data);
111  }
112 
113  case RAW_COLON:
114  return formatColonData(data);
115 
116  case RAW_COLON_WITH_VERIFY:
117  return formatColonDataWithValidation(data);
118  }
119 
120  return null;
121  }
122 
123  public static int packValue(boolean signed, int value, byte[] dst, int startIndex)
124  {
125  return packValue(signed, BigInteger.valueOf(value), dst, startIndex);
126  }
127 
128  public static int packValue(boolean signed, long value, byte[] dst, int startIndex)
129  {
130  return packValue(signed, BigInteger.valueOf(value), dst, startIndex);
131  }
132 
133  public static int packValue(boolean signed, BigInteger value, byte[] dst, int startIndex)
134  {
135  Boolean negative = null;
136  if(!signed && value.signum() < 0)
137  {
138  throw new RuntimeException("Value must be unsigned: "+value);
139  }
140 
141  if(signed)
142  {
143  if(value.signum() < 0)
144  {
145  value = value.negate().subtract(BigInteger.ONE);
146  negative = true;
147  }
148  else
149  {
150  negative = false;
151  }
152  }
153 
154  byte[] re = value.toByteArray();
155  if(re[0] == 0)
156  {
157  re = Arrays.copyOfRange(re, 1, re.length);
158  }
159 
160  return packValue(negative, re, dst, startIndex);
161  }
162 
163  public static int calcPackReqBytes(boolean signed, BigInteger value)
164  {
165  if(signed && value.signum() < 0)
166  {
167  value = value.negate();
168  }
169  return calcPackReqBytes(signed, value.toByteArray());
170  }
171 
172  public static int calcPackReqBytes(boolean signed, byte[] value)
173  {
174  int sReq = 9*(value.length-1) + (value.length-1)/7;
175 
176  int lst = 0xff & value[0];
177 
178  boolean msb = (lst & 0x80) == 0x80;
179 
180  while(lst != 0)
181  {
182  ++sReq;
183  lst >>>= 1;
184  }
185 
186  if(msb)
187  {
188  ++sReq;
189  }
190 
191  if(signed)
192  {
193  ++sReq;
194  }
195 
196  sReq += 8;
197  sReq /= 8;
198 
199  return sReq;
200  }
201 
202  /**
203  * packed byte scheme:
204  * XN------|X-------|X-------
205  *
206  * first byte:
207  * X extended address (0 = no, 1 = yes)
208  * N negated value (0 = no, 1 = yes)
209  *
210  * n'th byte:
211  * X value continued in the next byte + 7 bit number value
212  *
213  * negative:
214  * null: unsigned value
215  * false: signed positive
216  * true: signed negative
217  * */
218  public static int packValue(Boolean negative, byte[] value, byte[] dst, int startIndex)
219  {
220  if(0 == value.length)
221  {
222  if(Boolean.TRUE == negative)
223  {
224  dst[startIndex] = 0x40;
225  }
226  else
227  {
228  dst[startIndex] = 0;
229  }
230  return 1;
231  }
232 
233  int sReq = calcPackReqBytes(null != negative, value);
234 
235  int off = 0;
236  int index = value.length-1;
237 
238  for(int i = sReq-1;i >=0;--i,--index)
239  {
240  int val = 0;
241  if(0 == off)//value from the same byte
242  {
243  val = value[index];//bit offset
244  }
245  else//different bytes
246  {
247  val = (0xff & value[index+1]) >> (8-off);
248  if(index >= 0)
249  {
250  val |= (0xff & value[index]) << off;
251  }
252  }
253 
254  val &= 0x7f;
255 
256  if(++off == 8)
257  {
258  off = 0;
259  index += 1;
260  }
261 
262  if(i != sReq-1)
263  {
264  val |= 0x80;
265  }
266 
267  if(Boolean.TRUE == negative && i == 0)
268  {
269  val |= 0x40;
270  }
271 
272  dst[startIndex+i] = (byte) val;
273  }
274 
275  return sReq;
276  }
277 
278  public static byte[] packInt(boolean signed, int value)
279  {
280  byte[] ret = new byte[5];
281  int r = packValue(signed, value, ret, 0);
282  return Arrays.copyOf(ret, r);
283  }
284 
285  public static BigInteger unpackValue(boolean signed, byte[] data, int startIndex)
286  {
287  return unpackValue(signed, data, startIndex, null);
288  }
289 
290  public static BigInteger unpackValue(boolean signed, byte[] data, int startIndex, int[] usedBytes)
291  {
292  int ahead = 0;
293  try
294  {
295  while((data[startIndex+ahead] & 0x80) > 0)
296  {
297  ++ahead;
298  }
299  }
300  catch(ArrayIndexOutOfBoundsException out)
301  {
302  throw new RuntimeException("Incomplete value in the buffer.");
303  }
304 
305  final int used = ahead;
306  BigInteger ret = null;
307 
308  int cut = 0xff & ~((signed?0x40:0x00) | 0x80);
309 
310  final byte cut_first = 0x7f;
311 
312  int req = ((ahead+1)*7)/8+1;
313 
314  byte[] num = new byte[req];
315 
316  int off = -1;
317  for(int index = num.length-1;index >= 0&& ahead >= 0;--index, --ahead)
318  {
319  if(7 == ++off)
320  {
321  off = -1;
322  index += 1;
323  continue;
324  }
325 
326  int val = ((index != startIndex?cut_first:cut) & data[startIndex+ahead]) >>> off;
327 
328  if(ahead > 0)
329  {
330  int prev = ((cut_first & data[startIndex+ahead-1]) << (7-off));
331  /*System.out.println
332  (
333  index+"/"+off+": "+Integer.toBinaryString(prev)
334  +" | "+Integer.toBinaryString(val)
335  );*/
336  val |= prev;
337  }
338 
339  //System.out.println(index+" "+Integer.toBinaryString(val));
340  num[index] = (byte) val;
341 
342 
343  }
344 
345  ret = new BigInteger(num);
346  //System.out.println("unpack: "+Arrays.toString(num)+" "+ret);
347 
348  if(signed && (data[startIndex] & 0x40) > 0)
349  {
350  ret = ret.negate().subtract(BigInteger.ONE);
351  }
352 
353  if(null != usedBytes && usedBytes.length > 0)
354  {
355  usedBytes[0] = used+1;
356  }
357 
358  return ret;
359  }
360 
361  public static byte[] getValidPacket(byte[] e)
362  {
363  if(e.length > 0 && UartbusTools.crc8(e, e.length-1) == e[e.length-1])
364  {
365  return Arrays.copyOf(e, e.length-1);
366  }
367 
368  return null;
369  }
370 
371  public static boolean isPacketCrc8Valid(byte[] data)
372  {
373  if(data.length < 1)
374  {
375  return false;
376  }
377  return UartbusTools.crc8(data, data.length-1) == data[data.length-1];
378  }
379 
380  public static void printPacketStdout(byte[] data)
381  {
382  if(0 != data.length)
383  {
384  boolean valid = UartbusTools.crc8(data, data.length-1) == data[data.length-1];
385  System.out.println((valid?"":"!")+UartbusTools.formatColonData(data));
386  }
387  }
388 
389  public static void initConnection(UartbusConnection conn)
390  {
391  if(null == conn)
392  {
393  return;
394  }
395 
396  try
397  {
398  conn.init();
399  }
400  catch(Throwable t)
401  {
402  Object str = conn;
403  if(Proxy.isProxyClass(conn.getClass()))
404  {
405  str = "Proxy: "+System.identityHashCode(conn);
406  }
407  LoggingTools.tryLogFormatException
408  (
409  LOG,
410  LogLevel.NOTICE,
411  t,
412  "Exception while calling `%s`.call(). This might happened beacuse you connect to and older version of connection implementation.",
413  str
414  );
415  }
416  }
417 
418 }
static int packValue(Boolean negative, byte[] value, byte[] dst, int startIndex)