谈谈我的博世hackathon感受

一年一度的博世黑客马拉松要开始了,极客圈的小伙伴们都在跃跃欲试。好些个朋友都来问我这个参加过的选手,诸如xdk是啥啊,容易上手吗等问题,我干脆写到我自己的技术日记,要看随取。这里的确要吐槽下博世,的确宣传力度和技术社区培养不够,资料的确难找,不像STM32啊,Arduino啥的开源智能硬件,有强大的社区支持,例程和开发人员非常多。这也让xdk这个好东西让极客们有些陌生感。

粗暴的说,XDK是博世推出的IoT平台开发套件,它包含一个高度集成化的硬件终端,一些中间件,IDE和云平台。XDK秉承德国人一贯的严谨性,慢工出细活,东西是真TMD的精致,耐操。为啥这么说呢,大家都来IoT领域布局,所以都得有自己的独门绝技吧,比如,阿里拿手活儿是云计算,腾讯么靠强大的社交能力,这不Robin Li 要转型,祭出哥要扛起AI大旗杀手锏,结果一瓶矿泉水披头而下。那么德国博世的杀手锏则是传感器,传感器,传感器!

这个小盒子就是xdk的终端设备,它不是原型,它是一个实实在在的产品,而且是工业级的!工业级什么概念呢,自行百度吧。按产品设计的苛责级别是这样的:军工级别>车规级别>工业级别>消费电子级别。哥也参加过不少类似编程活动,现场给各种传感器模块,都是几块到几十块钱的廉价传感器,什么光敏电阻光传感器,PIR人体传感器,温湿度等等,我觉得和这个比起来呢,就是小孩玩具与成人级电子产品的差距,那些只能拿来玩一下丢掉,就是一个字,飘….。这个小盒子里集成了微机电加速度计,磁力计,陀螺仪,以及温湿度,压力,声光的传感器,还有2.4G蓝牙和WIFI互联方案,天线,卡槽和锂离子电池。这些都在一个指甲盖大小的电路板上集成!我比赛那会儿问专家,这么小,那诸如温度传感器自身的热量导致的数据准确怎么保证呢?专家说,我们有专门的补偿算法,可以直接用。请收下我的膝盖,瑟瑟发抖。这玩意儿除了贵,没其他毛病。我估计这个设备要卖一到两千元,因为它是面向工业级的。随贴随用,低功耗。

另外我简单看了下,板子上跑的应该是FreeRTOS,USB上电后,自动识别设备,直接可以和PC上的开发套件通信。PC的开发套件呢,其实就是基于eclipse CDT做的插件开发,细心的德国人几乎把你能想到的用户需求都给你做了,开发套件里面集成好了交叉编译链,烧写和调试等工具,而且包含大量的例程,每种外设都有例程,现场编程基本只需要把例程代码给copy出来通过编译,然后简单配置下外设即可一键烧写,开始调试,寄存器watch,断点等等,十分方便。这里截几个图给大家体验下。

开发时,你的数据总得上云啦。去年呢,博世时选择了物联网供应商sigfox,sigfox会发给你一个基于Arduino的EV板,然后给你提供了一个USB doogle的模拟基站,把Arduino和xdk板随便找几个rxtx杜邦线一戳,数据就来了,Arduino随便把示例程序改一下,烧一下,就可以和USB doogle的模拟基站通信了。PC上的模拟基站呢,其实很简单,用nodejs写的一个中转程序,通过浏览器配置好后,数据自动会被模拟基站利用PC的网络以HTTP方式发送出去。你只要配置发送目标例如:http://youip:port/youapi?name=$0&….$1..$2,用占位符就把数据传出去了。一次http get请求可以传152字节(记不太准确了)。今年好像没用这个了,这让你发挥空间更大,请极客们自行携带你的爱板,随便玩。

至于云端嘛,博世的云端时和微软Azure合作的,部署在微软云上,叫Bosch IoT Suite,这个其实你可以用可以不用,如果你对服务器熟悉,你完全可以随便把你自己的阿里云,腾讯云啥的http服务搞一个在那里,写几行php啊,nodejs或者servlet就行了。坦白讲,微软云的东西吧,用户接口很不好,不符合中国人的使用习惯,资源入口很难找,中文网站像机器翻译的一样。

我觉得博世的赛题也挺好的,可以充分发挥其传感器的作用。这里给小伙伴们点建议,比如电梯场景,可以利用加速度计检测电梯的运行速度,因为电梯可以调节运行加速度,商场等场所就很快,住宅楼则慢很多,可以用陀螺仪检测意外的轿厢抖动,还有可以利用光线传感器检测开门关门次数啊啥的,再检测些温湿度等工作环境啥啥。随便组合一下,做个电梯运行健康估值啊,异常情况预警啊,老化预测啊等等。我保证你带1万奖金回家。哈哈。。。。

(XDK_Getting_Started_v2 附上一份getting started指南,拿走不谢)

 

 

Jquery Easyui中的坑

Jquery Easyui是一个不错的前端UI框架,它以非常低的学习和编程成本,尽可能的用非标准的HTML属性和特殊的css来标记页面,由框架统一解析为前端UI控件。
但是,框架的作者采用了类似半开源的方式,license可以使的软件免费使用,但是,它对javascript源码进行了混淆,不发布源码,文档虽然还算整理的不错,但是一旦遇到坑,那就十分捉急。但是,我对其用来做demo的速度赞不绝口,所以总结各种坑与大家共享如下:

坑1:DataGrid如何使用动态的查询参数?
场景如下,一个Datagrid上额外增加了几个表单参数,要动态的使用这些参数,就很麻烦。
如果你使用:

$('#orderGrid').datagrid('reload',
{
//参数对
});

这种情况下,每个表单的onchange都不想这么调用一遍,代码十分重复,而且一旦表单多,逻辑复杂,基本不可能实现。

另外一种情况是使用queryParams,但是这个参数设置后就不变了,是静态参数,
var orderGrid = $('#orderGrid').datagrid({
queryParams: {
startDate: $('#startDate').datebox('getValue'),
endDate:$('#endDate').datebox('getValue')
}
});

实现从queryParams入手,
一,获得到参数
var param=$('#orderGrid').datagrid('options').queryParams;
二,修改参数
param.startDate = ... ... ;
param.endDate = ... ...;

三,把参数设置回去(这一步很重要只有重设才会触发更改!)
$('#orderGrid').datagrid('options').queryParams=param;

坑二:datagrid的$(‘#orderGrid’).datagrid(‘getChecked’)和$(‘#orderGrid’).datagrid(‘getSelection’)等方法获取选中不一致的问题。

选中事件onCheck, onUncheck, onCheckAll, onUncheckAll配合使用时,建议使用回调参数
onUncheck:function(index,row)的index和row参数来决定选中行。直接使用$(‘#orderGrid’).datagrid(‘getSelection’)等方法得到的选择状态莫名其妙的错误和不一致。

高效的字符串匹配器

使用场景

String html="......<script type="text/javascript.........</script>"";

StringMatcher matcher = new StringMatcher("</script>");


for(int i=0;i<html.length;i++){

    matcher.push(html.charAt(i));

    if(matcher.isTarget()){

        //do something

    }

}
/**
 * StringMathcer是一个利用了循环链表原理的数组型字符串匹配器.
 * 
 * Matcher主要是在字符串处理中匹配字串,原理是把字符push到匹配器中 进行比较,匹配器不提供remove(int index)方法,只能放或者清空。
 * 用单纯的数组进行匹配时,很容易地,数组会满而导致大量的元素移动操作, 代价交代,用循环链表只需修改两次指针位置即可。
 * 为了更多的减少条件判断,在构造方法中就已经让数组处于满的状态。
 * 
 * @author maoanapex88@163.com
 * 
 */
public class StringMatcher {
	char[] cache;
	int start;
	String target;

	public StringMatcher(String s) {
		this.target = s;
		int length = s.length();
		cache = new char[length];
		start = 0;
		for (int i = 0; i < cache.length; i++)
			cache[i] = 128;
	}

	public void push(char c) {
		cache[start++] = c;
		start %= cache.length;
	}

	public boolean isTarget() {
		if (cache[(start + 1) % cache.length] == 128)
			return false;
		int index = 0;
		for (int i = start; i < cache.length; i++, index++)
			if (cache[i] != target.charAt(index))
				return false;
		for (int i = 0; i < start; i++, index++)
			if (cache[i] != target.charAt(index))
				return false;
		return true;
	}

	public boolean isTarget(boolean ignoreCase) {
		if (cache[(start + 1) % cache.length] == 128)
			return false;
		int index = 0;
		for (int i = start; i < cache.length; i++, index++) {
			if (ignoreCase && cache[i] != target.charAt(index)
					&& cache[i] - target.charAt(index) != 32
					&& cache[i] - target.charAt(index) != -32)
				return false;
		}
		for (int i = 0; i < start; i++, index++) {
			if (ignoreCase && cache[i] != target.charAt(index)
					&& cache[i] - target.charAt(index) != 32
					&& cache[i] - target.charAt(index) != -32)
				return false;
		}
		return true;
	}

	/**
	 * clear
	 */
	public void clear() {
		start = 0;
		for (int i = 0; i < cache.length; i++)
			cache[i] = 128;
	}

	/**
	 * @return
	 */
	public int length() {
		return target.length();
	}
}

为Java实现LinkedArray

前面的文章中,我们通过分析java的排序,顺便分析了ArrayList和LinkedList,这两个各有优缺点,那么我们能不能折合两者优点呢?我思考了一个问题-信息率,也就是为了存一个我希望存的对象而额外耗费的空间,

  • 对于ArrayList,每个数组的下标对于的地址单元存量一个对象指针,1:1的存,但是问题是在于增量导致的数组增长和整体数组的copy实在太损耗性能
  • 对于LinkedList,每一个链表节点,要额外增加一个前续来维持链表结构,1:2,事实JDK的LinkedList是prev和next都记录的,是双向链表,为1:3

既然有这个问题,我们可不可以增加信息率来提高些平均性能呢?如下

  • ArrayList: [a0,a1,a2,…,an]
  • LinkedList:  a0<->a1<->a2<->…..an
  • LinkedArray: [a0,a1,a2,….ai]<->[ai+1,….aj]<—>[aj+1,………an]

LinkedArray是用链表为基础,但是链表的节点是一个数组,这样节点的信息率上升为n:n+2,(n是数组的大小),这样可以这种一些两者的性能。

package net.sourceforge.util;

import java.util.AbstractList;
/**
 * 
 * @author maoanapex88@163.com
 * 
 * @param <E>
 */
public class LinkedArray<E> extends AbstractList<E> {
	public static final int DEFAULT_ARRAY_SIZE=10;
	public static final int DEFAULT_CATACITY=10;
	
	private int arraySize=DEFAULT_ARRAY_SIZE;
	private int size;
	
	private transient ListNode<E> head,tail;
	
	public LinkedArray(){
		this(DEFAULT_CATACITY,DEFAULT_ARRAY_SIZE);
	}
	public LinkedArray(int initialCapacity){
		this(initialCapacity,DEFAULT_ARRAY_SIZE);
	}
	public LinkedArray(int initialCapacity, int arrayLengthInNode){
		if(initialCapacity<=0) throw new IllegalArgumentException("initialCapacity < 0");
		if(arrayLengthInNode<=0) throw new IllegalArgumentException("arrayLengthInNode < 0");
		
		arraySize=arrayLengthInNode;
		
		int nodeCount=initialCapacity/arrayLengthInNode;
		if(initialCapacity%arrayLengthInNode!=0) nodeCount++;
		
		ListNode<E> pre=null;
		for(int i=0;i<nodeCount;i++){
			ListNode<E> node=new ListNode<E>(arraySize);
			if(i==0) head=node;
			else{
				pre.next=node;
				node.previous=pre;
			}
			
			if(i==nodeCount-1) tail=node;
			pre=node;
		}
		size=0;
	}
	
	@Override
	public E remove(int index) {
		rangeCheck(index);
		ListNode<E> node=findNodeByIndex(index);// must not be null
		E ret=node.objArray[index-node.globalStartIndex];

		int start = index - node.globalStartIndex + 1;
		for (int i = start; i < node.localSize; i++) {
			node.objArray[i - 1] = node.objArray[i];
		}
		node.objArray[arraySize - 1] = null;
		node.localSize--;
		
		if(node.localSize==0) {// remove the empty node
			ListNode<E> pre=node.previous;
			ListNode<E>  next=node.next;
			
			if(pre==null){// equals node is the head node
				node.previous=null;
				head=node;
				updateNode(head,0);
			}
			else{
				pre.next=next;
				if(next!=null) next.previous=pre;
				
				updateNode(pre.next,pre.globalStartIndex+pre.localSize);
			}
		}
		
		else updateNode(node.next,node.globalStartIndex+node.localSize);
		
		size--;
		return ret;
	}
	@Override
	public E set(int index, E element) {
		rangeCheck(index);
		ListNode<E> node=findNodeByIndex(index);// must not be null
		E ret=node.objArray[index-node.globalStartIndex];
		node.objArray[index-node.globalStartIndex]=element;
		return ret;
	}
	@Override
	public void add(int index, E element) {
		if (index > size || index < 0) throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
		ListNode<E> node=findNodeByIndex(index);
		if(node==null){// equals to index==size
			if(tail.localSize==arraySize){//tail node's array is full, append a node
				ListNode<E> newNode=new ListNode<E>(arraySize);
				newNode.globalStartIndex=size;
				tail.next=newNode;
				tail=newNode;
			}
			tail.objArray[tail.localSize]=element;
			tail.localSize++;
			if(size==0) head.globalStartIndex=0;
		}
		else{//insert in the middle of node list
			if(node.localSize==arraySize) {//node's array is full
				ListNode<E> next=node.next;
				ListNode<E> newNode=new ListNode<E>(arraySize);
				node.next=newNode;
				newNode.previous=node;
				
				newNode.next=next;
				if(next!=null) next.previous=newNode;
				
				newNode.objArray[newNode.localSize]=element;
				newNode.localSize++;
				updateNode(newNode,node.globalStartIndex+node.localSize);
			}
			else {//node's array is not full
				node.objArray[node.localSize]=element;
				node.localSize++;
				updateNode(node.next,node.globalStartIndex+node.localSize);//node.next must not be null
			}
		}
		size++;
	}
	private void updateNode(ListNode<E> node, int myGlobalStartIndex) {
		if(node==null) return;
		node.globalStartIndex=myGlobalStartIndex;
		if(node.localSize!=0) updateNode(node.next,node.globalStartIndex+node.localSize);
	}
	
	public E get(int index) {
		rangeCheck(index);
		ListNode<E> cur=findNodeByIndex(index);
		if(cur==null) throw new IllegalStateException("This should be an inner error, findNodeByIndex can not return null when used by get method. index="+index);
		return cur.objArray[index-cur.globalStartIndex];
	}

	public int size() {
		return size;
	}
	private final ListNode<E> findNodeByIndex(final int index){
		ListNode<E> cur=head;
		while(cur!=null) {
			if(cur.globalStartIndex+cur.localSize>index) return cur;
			cur=cur.next;
		}
		return null;
	}
	private final void rangeCheck(int index) {
		if (index<0||index >= size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: "+ size);
	}
	
	public String toString() {
		StringBuilder builder=new StringBuilder();
		ListNode<E> node=head;
		builder.append("{").append(size).append(",");
		while(node!=null) {
			builder.append(node.toString()).append("->");
			node=node.next;
		}
		builder.append("}");
		return builder.toString();
	}
	
	private static class ListNode<E> {
		static final int NULL=-1;
		transient E[] objArray;
		ListNode<E> previous,next;
		int globalStartIndex=NULL, localSize=0;
		@SuppressWarnings("unchecked")
		public ListNode(int arrayLength) {
			super();
			objArray=(E[])new Object[arrayLength];
		}
		@Override
		public String toString() {
			StringBuilder sb=new StringBuilder();
			sb.append("(")
			.append(globalStartIndex).append(",")
			.append(localSize).append(",[");
			for(int i=0;i<localSize;i++) sb.append(objArray[i]).append(",");
			sb.append("])");
			return sb.toString();
		}
	}
}