最近动态

编程

安卓初体验?

安卓初体验~

其实这并不算是初体验吧,毕竟大三的时候已经玩过android开发了,而且看起来也蛮像回事儿的.

只不过当时搞了NFC,业务逻辑和网络请求.剩下的最重要的界面和界面跳转,都是在鹅厂的望神帮我写的~

第一节课其实我没去

那一节课,据说老师讲了布局文件

然后好像就布置了一个计算器的作业,然后就没什么了.

反正,我就在界面编辑器里面拖控件.然后findViewById,之后给他们做了一些逻辑~

第二节课说是要做SD卡读取图片,并且显示出来

我一想,觉得有点儿难啊,于是就开始查官方文档去了,

整理了一下,获取文件夹的路径,文件夹内文件这些好像都不难.

选择一个内容显示估计也不难,但是如何做一个漂亮的选择页面,可能比较复杂.

我打算用GridView,做一个和相册一样.

Let’s do this

先搞了一个SDCardHelper的类(写iOS时候留下的习惯,各种Helper)

//外部储存是否可写
static public boolean isExternalStorageWritable()
//外部储存是否可读
static public boolean isExternalStorageReadable()
//储存路径
static public String storagePath()
//为GridView用的文件列表
static public List<GridItem> getFileList()

Android的GridView和iOS的UICollectionView好像差不太多

Android的GridView需要的适配器里面需要重写

public int getCount();
public Object getItem(int position);
public long getItemId(int position);
public View getView(int position, View convertView, ViewGroup parent);

iOS的UICollectionView需要实现DataSource协议的

-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView;
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section;
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

这样就可以正常显示一个GridView(UICollectionView)

可是,我要自定义控件呀!不然用原始的GridView中每个item的View或者UICollectionView中原始的UICollectionViewCell
,那得多难看…

在iOS中,只要写一个继承于UICollectionViewCell的类,对它进行界面操作,数据操作,并且在Protocol中复用Cell并且返回,就可以完成目的

在Android中,貌似没有类似UICollectionViewCell的东西.所以得自己重写一个类,用来持有Item的xml布局文件中的各个控件

import android.widget.ImageView;
import android.widget.TextView;

/**
 * Created by vikingwarlock on 16-9-23.
 * This is the class for view
 */
public class GridItemView {
    public ImageView imageView;
    public TextView filename;
}

之前的图片列表,也专门写了一个类,用来存放这个数据

import java.io.Serializable;

/**
 * Created by vikingwarlock on 16-9-23.
 * This is a class that represent a grid item
 */
public class GridItem implements Serializable{
    public String filepath;
    public String filename;
}

这里用了一个Serializable,是为了后面Intent传递用的~

最后在Adapter类里面实现View

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    GridItemView item;
    GridItem data = gridItemList.get(position);
    if (convertView == null) {
        convertView = inflater.inflate(R.layout.gridviewitem, null);
        item = new GridItemView();
        item.filename = (TextView) convertView.findViewById(R.id.itemName);
        item.imageView = (ImageView) convertView.findViewById(R.id.itemImage);
        convertView.setTag(item);
    } else {
        item = (GridItemView) convertView.getTag();
    }
    ImageLoader.getInstance().displayImage("file://"+data.filepath,item.imageView);
    item.filename.setText(data.filename);
    return convertView;
}

界面就差不多搞定了~

至于点击效果,iOS依然是实现一个Protocol~就可以了

-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;

Android需要手动添加

GridView gv = (GridView) findViewById(R.id.gridView);
if (gv != null) {
    gv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
            GridItem item = datalist.get(position);
            Intent intent=new Intent();
            intent.setClass(SDView.this,MainActivity.class);
            Bundle bundle=new Bundle();
            bundle.putSerializable("result",item);
            intent.putExtras(bundle);
            setResult(2,intent);
            finish();
        }
    });

这里的gv是GridView的意思,不要想歪了.

做了一堆和Bundle有关的事情,主要是为了Intent中可以传递一个对象,而不仅仅是字符串.

setResult()用来传递一个Intent回上一个需要Result的Activity.

finish()就是结束当前Activity.

这个花了我最多时间的东西就这样做完了~

当然啦,后面还遇到了图片显示的问题.比如卡住啊,比如OOM啊~最后借助了universal-image-loader
都解决了!!

最后坐在旁边的同学跟我说,他们的代码,只有几十行.原来老师给的demo中获取图片是这样写的

@Override
public void onClick(View v) {
    Intent intent = new Intent();
    intent.setType("image/*");
    /* 使用Intent.ACTION_GET_CONTENT这个Action */
    intent.setAction(Intent.ACTION_GET_CONTENT);
    /* 取得相片后返回本画面 */
    startActivityForResult(intent, 1);
}

我好生气!说好的操作SD卡呢!

这样的写法,和iOS中的UIImagePickerViewController一样了~

Anyway,至少我学会了自定义控件的GridView,体验了一把OOM,还学习了SDCard的操作

还望各位大大指导我!

阅读剩下更多

日记

We do what we want to do

Solo

之前鼓励大学生创业.一开始我觉得这是很有趣的事情,觉得大学生终于可以抛开书本,抛开各种习题,去做一些有实际意义的事情了

然而,那一年死了很多创业团队;我觉得一来是因为扛不过大公司,二来是太浮躁.

我承认我在某些方面是很浮躁的一个人,很容易自我膨胀.所以我不适合去管理一个公司.

膨胀 Begin

不过在做产品方面,我还算是个静得下心得人,所以来整理整理功能模块,调整各部分的交互方式.我还是可以胜任的吧

膨胀 End

扯远了…这篇日记是来整理一下,开学第3(还是4)周,一边学习一边工作的心得.

This is tough

说真的,一边学习一边创业,真的是很难的事情.

我们周Boss,为了公司,牺牲了他的全部.完全没有上课,基本没有休息日,晚睡早起,同时处理很多部分的事情

过去公司在校内,下课去公司,几分钟就好了;现在由于规模扩大,公司搬到了校外的孵化园,虽然环境好了很多,网速也快了许多,周边生活设施也很便利,但是.

离学校太远了,离我的寝室更远!

我偷偷地承认一下,我也会翘一些一些课…

又扯远了…

This is tough, 最难的不是说我们做的东西做不出来.

而最难的是,我们能够高效快速的把我们想做的东西做出来,并且达到预期的效果

为了达到效果,一遍一遍的修改,一遍一遍的推翻重来,一遍一遍的联调修Bug…

I thought

曾今我优化代码,提升App性能,只有一个目的,让用户喜欢iOS,远离Android,因为我是个果粉.所以我喜欢偷偷擅自主张的添加一些小细节,来让用户使用的更加舒服,更加方便.

In fact

现在,我们为了共同的梦想在奋斗.我们想打造一个不一样的自行车世界,也想帮助更多的传统行业进入物联网时代.

为了这个梦想,我们可以牺牲娱乐,可以牺牲一些学业,可以牺牲睡眠.

可以通宵,可以不放假,可以没有薪水,可以一个人干多个人的活儿.

因为,我们愿意这样做

We want to do this.

Sorry

本来想写一篇鸡汤文,来鼓励爱浪的小朋友们,多学习多实践,多提升自己的能力,踏踏实实的度过大学时光.

结果变成了一篇给自己加油的日记了~

I hope I can fix the bugs tomorrow.

阅读剩下更多

编程

BLE的广播帧

之前还以为BLE也就GATT那些东西嘛,没什么好说的了

其实

BLE或者说,蓝牙,的广播帧还是有很多很多奥秘的.还是有些东西可以拿来琢磨琢磨,学习学习

Context

至于为什么要突然学习这个东西,肯定是和新产品有关系的,具体的,可以等上市之后,买来一个试试看啊,哈哈哈哈哈哈

另一个原因,这个东西的官方文档真的很难找,至少在 www.bluetooth.com 上面我就没有看到讲述广播帧结构的文档,最后在TI的一个文档中,终于找到了相关的内容.

平时用BLE只要是在连接成功之后,搜索Service和Characteristc,后进行读写操作.这样的工作模式,只能一对一,不能做到一对多.但是利用了广播信息,可以做到一对多的发送消息…虽然这时候不能反过来写入.

Let’s go

我们先来上一张图片,看看Bluetooth到底广播了什么吧

AD

我是做上位机软件开发的,所以只关心应用层的东西,那么上面图片中,iOS和Android能被处理的数据是标红的那一段

一共31个字节,看起来好像不是很足够,但是由于协议是规定好了的,所以对应的位置表示对应的数据,还是够用的.

说说我们熟悉的一些广播信息吧:(iOS端CoreBluetooth的广播帧字典的Key)

1.LocalName (外设名称)
2.ServiceUUIDs (Service列表)
3.ManufacturerData (制造商数据)
4.IsConnectable (是否可连)

下面再给点儿我第一次知道的广播信息类型:

1.ServiceData (某个Service的广播数据)
2.TxPowerLevel (传输信号强度~.~,用来计算损耗用...好像暂时没啥用处,反正我也不会算)
3.OverflowServiceUUIDs (没懂他和ServiceUUIDs的区别~.~)
4.SolicitedServiceUUIDs (貌似和从设备没啥关系~.~)

说实话,平时用到的只有ServiceUUIDs,因为我们用这个来过滤出来我们需要的蓝牙外设,也用这个来区分不同的硬件.

LocalName的话iOS和Android都已经自动提取并封装成简单的接口了

上面是软件层面的东西,我们来看看硬件层面的呗~

一个广播帧会有很多类型的数据,每个类型有自己的type,上位机也是通过type字段来分辨数据是UUID还是Name或者其他的东西~

每一类数据都由3部分组成:

1.length of data (这个type的总长度)
2.type (定义type)
3.data (根据type的协议来填入数据)

这里简单的列举几个type:

0x02 不完整的16位UUID
0x04 不完整的32位UUID
0x06 不完整的128位UUID
0x08 设备全名
0x16 16位UUID的Service Data
0x20 32位UUID的Service Data
0x21 128位UUID的Service Data
0xFF 制造商自定义的数据

Example of Peripheral and Bluetooth Central

一台名叫VK的外设,公布了一个Service UUID 3344,5566;其中3344的Service Data是”0xAA,0xBB,0xCC”,制造商的自定义数据是”0x12,0x34,0x56,0x78”

那么他的广播数据是~

04 08 56 4B 05 02 44 33 66 55 06 16 44 33 cc bb aa 07 FF 00 00 78 56 34 12

拆分数据分析分析

04 08 56 4B
长度4 设备完整名称 V K

05 02 44 33 66 55
长度5 不完整的16位列表 3344 5566

06 16 44 33 cc bb aa
长度6 16位UUID的ServiceData UUID=3344 数据是 AA BB CC

07 FF 00 00 78 56 34 12
长度7 厂商自定义数据 厂商ID=0000 厂商自定义数据=12345678

如果有错误不要打我…

iOS端解析广播数据

-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI
{
    // 名字的字符串
    NSLog(@"Name %@",advertisementData[CBAdvertisementDataLocalNameKey]);
    // CBUUID的NSArray
    NSLog(@"Service UUID %@",advertisementData[CBAdvertisementDataServiceUUIDsKey]);
    // Key为CBUUID,Value为NSData的NSDictionary
    NSLog(@"Service Data %@",advertisementData[CBAdvertisementDataServiceDataKey]);
    // 制造商的数据NSData
    NSLog(@"ManufacturerData %@",advertisementData[CBAdvertisementDataManufacturerDataKey]);
}

Android端解析

@Override
    public void onLeScan(final BluetoothDevice device, int rssi,
            byte[] scanRecord) {
        // scanRecord 就是所有的广播数据呗
   }

iOS端为了隐私保护,做了各种各样奇怪的限定,所以不像Android那么爽,可以直接把广播帧的字节流都提取出来,所以就只能好好阅读协议,才能让iOS的App获取到想要的数据

相同的,Android虽然数据很开放,下位机可以不按照BLE协议去设置广播数据,Android App依然可以读得出来数据,但是解析数据就很头疼的样子了.

嗯,各有各的好处~

BTW

其中一个type是

0x17  Public Target Address
目标Mac地址

官方定义是

The Public Target Address data type defines the address of one or more intended recipients of an advertisement when one or more devices were bonded using a public address. This data type is intended to be used to avoid a situation where a bonded device unnecessarily responds to an advertisement intended for another bonded device.

what are you 弄啥嘞…

我YY他的用处就是,这个BLE设备只能被指定Mac地址的主机连接.

好啦,这次又学到了一点儿蓝牙的知识,写个文章当笔记.

阅读剩下更多

什么鬼

纠结的随想

矛盾存在于一切事物中,并且贯穿于事物发展过程的始终,即矛盾无处不在,矛盾无时不有

这矛盾,大概也称为纠结

我们每天都可能纠结很多的事情,有的是大事儿,有的是小事儿.小事儿也可以称为矫情 ,比如说:

1.今天吃咩啊?
2.看哪部电影啊?
3.今天要不要洗衣服啊?
4.....

这些矫情的小矛盾出现在~选择困难症的人的日常生活中,或者当一件事情的两个选项,会牵扯到另外一些矫情的事情的时候

上面都是废话

这篇日志,主要是说说最近遇到的一些小故事吧

麦当劳不搭配套餐事件

某一天去麦当劳,前面排着一个老奶奶,拿着一张餐巾纸,上面写着:

巨无霸,鸡翅,可乐,大鸡排

老奶奶拿出老花眼镜来慢吞吞的读着纸巾上的字,收银员有点儿不耐烦,于是就直接看着纸巾帮她点餐了.

我看着纸巾上的内容,觉得有点儿眼熟,好像是一个套餐+大鸡排,但是服务员最后给的价格远远超出我的想象,50+

这时候我的大脑开始了严密而紧张的计算,最后得出结论,如果不用单点,而是使用套餐,可以节省9元钱左右

这貌似和本文又无关了

No,我计算完毕并且验算完毕之后,一直很纠结,要不要当着大家的面指出这个事情呢?

我总觉得他们是在欺负老人,我最讨厌欺负老人的人了.

往往在这内心矛盾,纠结选项的时候,事情就过去了.老奶奶拿着食物离开了餐厅.

这件事情我憋了很久.或许正如一个朋友说的那样,收营员不熟悉菜单,不知道这个是套餐;又或许和我的理解一样,收营员是故意的,觉得老奶奶没说起,就不用管她.

归根结底,假如收银机智能一些,自动的规划成套餐;假如我早一点儿看透这一切,帮老奶奶来点餐.说不定也不至于这么耿耿于怀了吧~

开学之各种纠结的事情

开学是个新的开始,一旦出现新的开始,大家就会忙着制定计划,这时候也会纠结迷茫.

有的人纠结事情多,不知道怎么分配精力
有的人纠结未来能做些什么
有的人同时纠结上述两项

来一句虽然有点儿难听,但是却又很真实的话

正正常常的读完大学依然不知道,自己可以做什么,自己会什么

这样的事情在我们IEC也会出现~

当然IEC的孩子不会不知道自己可以做什么,毕竟在IEC坚持下来的孩子,都能写得一手好代码,出去做个程序员,不是什么大问题

可即便如此,他们还是会在读研,出国,工作三者中纠结.

各有各的好,各有各的不好.出国比国内读研牛逼,但是费钱;读研比工作轻松,但是没有钱挣,过得很拮据

刚加入不久的孩子会在学业,课余生活和编程的权重比例上面纠结很久

听到他们在纠结的时候,我总是会觉得:这种事儿有什么好纠结的呢?

然而自己在两年前,也因为纠结这件事情,走了很多很多的弯路.

现在我来充当一下老司机吧~

没什么好纠结的!选择一个目标,不要改变,往下走,往里钻,一定会搞出些名堂的!

最后是自己纠结的事情

可能是第一次,我不纠结学习和工作的事情……

因为,

这次是玩耍和工作起了矛盾~

回想一下大学的4年,我从来没有离开过成都.没有看过大熊猫,没有去过锦里,没有去过各种名胜古迹.反而是其他城市读书的同学走过的地方更多,每次被杭州的朋友或者亲戚问起,成都好玩不?四川好玩不?

我总是无言以对,笑笑说,平时太忙了,没时间去玩.

其实,这并不是假话~

作业,工作,确实占用了很多的时间,而且把时间剁碎了,变得碎片化,找不到完整的可以用来出游的完整时间.

大概是应该我的性格,所以我没有崩溃,没有抑郁,反而津津乐道,觉得很有成就感.

可是今天听到了 起风了 里面的主题曲,突然脑子里面就是电影的画面,男主角是个牛逼的工程师,每天有画不完的图纸.但是他又经常可以在草原上看着飞机飞,可以感受风,可以用风来洗净自己,让自己可以在黑暗丑陋的时代里依旧很干净

当然,我没有那么高尚.只是我突然发现,自己并不是可以不用玩耍的人,我虽然喜欢工作,但也喜欢干净安静的风景,总给我一种很舒服的感觉,让我可以放松一下紧绷的心

今天话很多啊

最近写的代码,没啥技术含量,不说也罢,所以就来”文艺”一下自己

等这段时间的感性期过了,就可以继续post一些技术文章咯~

阅读剩下更多

什么鬼

开始研究生活

假期还是结束了,又回到了”熟悉”的校园,搬了两天的家,出了几顿的汗水.

躺下来的时候,发现,枕头没有,先垫几块布凑活凑活吧~

刚打开研究生寝室的门,我就惊呆了,这也忒小了吧…比本科生寝室的一半还要小…

不过通宵供电和超级爽的澡堂还不错,再也不用担心回到寝室,睁眼黑.也不用担心在浴室打开龙头出来的永远是冷水~

研究生的课表真的很恼火,培养方案特别难理解,只能走一步看一步了.

虽然我已经本科毕业了,但是还是很喜欢IEC的氛围,放心,我不会走的,哈哈哈,我要来充当2016级新生.

开学总是斗志满满的.一开学脑子里面就浮现出来了超级大学霸:赫敏.觉得自己也能和她一样不仅可以搞定很多很多课,而且还能兼顾公司的产品.不过,我确定,自己是没有这个水平的…

说到公司,我确实得在后面几天考虑考虑时间的分配,不然,会辜负SUP这个职称的!

这,已经开学5天了,我数了数,还是有一大堆事情没做,看来神经还没有绷紧.

不知道刚开学的你们,感觉如何,要不要也来好好地规划一下未来这几个月的生活呢?

VKWKMDZZ

阅读剩下更多

日记

监狱生活

~.~

这次JCY的项目总让我觉得和当年乐山的项目很像,

甲方都很变态!!

工期大幅度缩短,10月->9月->8月底->周一(那一周的周一)

功能也乱改,于是最终版出来的时候,我发现,我好像写了好多接口,最后都不用了~

于是我生气的不给你装PostGRE SQL,不给你装FastCGI!

JCY的办公室里面都开着空调,然而我的汗还是没有停过,感觉把一年的汗都流完了.

OK,讲讲故事吧

第二次使用windows server,也是第一次在windows server上面安装python,双击python的window安装包,一阵狂按回车,表现出很熟练的样子,然后…

boom 安装卡在了奇怪的地方

There is a problem with widows installer package,a program run as part of setup did not finish as excepted. contact your support personnel or package vendor. 

于是,进度条就退了回去.

然后,我就试了试32位的安装包,依然卡在了奇怪的地方,又退了回去.

注意到一个细节:

以前安装的时候好像会弹出一个控制台,在安装setuptool和pip,但是这次并没有弹出,所以我估摸着,是pip的问题,所以新的安装的时候,我把pip去掉了,于是神奇的成功了~

当然,没有pip和setuptool的话,就不能安装其他的模块了呢,所以只能再打开一次安装程序,安装pip.这次竟然没有错误!哈哈哈哈哈哈

于是就这么成功的搭建完毕.

JCY的食堂我竟然觉得还蛮好吃的~~

At Last

求这个活儿赶紧结束,放我回家

阅读剩下更多

编程

django服务器从linux迁移到windows

教研室接了一个检察院的项目

老实说,这个项目坑爆了

一开始说可以提供一个全新的服务器,系统也是我们来定,于是,嫌弃windows的我在Linux上面很熟练的搭建起服务器来

直到…

有一天,他们的技术人员告诉我 “由于经费紧张,咱们就不能买新的服务器了,以前的服务器你想要什么系统都可以”

于是报了三个Windows Server的版本给我…

好吧,反正我也没的选,那就先看看改到windows上面有哪些改变吧?

Context 那边的服务器由于安全问题,是不允许接入外网的

所以PIP都不能用了,所以先把需要的包给下了吧,什么Django,Arrow,Python-DateUtil……

windows上面没有uwsgi,那就用FastCGI吧

想得美,我才不为了你单独去搞FastCGI呢,Django跑跑就够了!!

Nginx还是要的,不然我还得在django里面写下载接口,好在Nginx是有Windows版的 good job

等下

Windows 没有自带 Python ~~

为了保险起见,把32位的和64位的都下载下来

Context 他们是不允许使用优盘的,所以一切的一切,都只能使用刻录的光盘…

OK,前面基本都是废话,简单说,就是在Windows上面装环境啦…

代码改动?

嗯,上传文件接口,Django的Media_Root需要改个位置,当然Nginx的下载地址要改个位置,还有之前使用uwsgi的,现在得换成Django

windows的nginx貌似是不会自动选择conf/nginx.cfg,所以启动的时候还得手动写

nginx.exe -c conf/nginx.cfg

本来是用uwsgi的,那现在就直接Django启动到指定端口

python manage.pyc runserver 9999

哦哦哦,pyc文件的生成方法也很重要,毕竟这个项目xxxx,所以我还是选择不提供源代码给他们.

那就得编译一下再给他们

python -m py_compile manage.py

以此类推,把他们都编程编译过的文件

注意

linux下一个路径是
xxx/xxx/xxx

windows下一个路径是

X:\xxx\xxx

你以为修改django代码的时候就应该把

xxx/xxx/xxx 改成 X:\xxx\xxx

其实…还是

X:/xxx/xxx

本来打算写一个shell来自动安装的,现在看来,只能写成bat文件了~

由于msi的安装不会用bat来做,所以还是手动安装一下比较好

@echo "Start Unzip python packages"
Unzip Django-1.9.9.zip -d C:\
Unzip python-dateutil-patches-for-1.5 -d C:\
Unzip arrow-master -d C:\
@echo "Install Nginx"
Unzip nginx-1.11.3 -d C:\
@echo "install python packages"
python C:\Django-1.9.9\setup.py install
python C:\python-dateutil-patches-for-1.5\setup.py install
python C:\arrow-master\setup.py install
@echo "Copy Nginx configure file"
copy nginx.cfg C:\nginx-1.11.3\conf\nginx.cfg
@echo "build download folder"
md "C:\file"

在启动之前添加一些用户,然后就能启动了.

windows server还是适合使用windows自己的服务器~ASP.net

阅读剩下更多

编程

蓝牙测速

之前的文章中介绍过蓝牙的速度和踏频协议,在那篇文章中也顺便介绍了BLE的一些小名词.这一篇文章就来说说炫轮App 3.0中的测速模块是怎么构成的吧.

Context

炫轮App可以获取到哪些传感器的数据呢?

  1. 炫轮车灯自身的码表数据
  2. 炫轮踏频器
  3. 满足公有踏频速度协议的传感器
  4. GPS

他们的数据是些什么?

1.炫轮车灯的数据

炫轮车灯会通过蓝牙得到每转一圈需要的时间

2.炫轮踏频器

这个产品不测速,所以先不多说~

3.满足公有协议的踏频速度传感器

通过蓝牙得到在第x秒转了y圈的信息

4.GPS

GPS数据本身就携带了速度信息,只不过,如果GPS信号不强,那这个数据就会不精准

再来个附加功能

大多数的骑行App的一次运动运动时间是手动点击开始,暂停,保存来记录的.这样不是很难过嘛?我想要个自动开始,自动暂停的码表!

不用往下翻,我没写~~这只是打个广告

Structure

所以和速度有关的传感器目前为止大概是那么3类: 炫轮车灯,满足公有协议的速度计,GPS

既然,GPS那么不稳定,干脆砍掉算了.

所以问题就变成了,如何根据炫轮车灯和速度计来计算出较为准确地速度.


Structure-Context

App 可以连接两个炫轮车灯,一个速度计

码表模块是和界面分离的,把他当做服务来处理(这里我写了一个单例),这个类可以收数据,也可以被获取数据.收到的自然是解析后的炫轮数据和速度计的数据,提供获取接口的是多种单位下的:当前速度,平均速度,总路程,总运动时间,(当然还有踏频和心率,只是这里不用,所以就不说了)


全局变量

计时器1枚,记录炫轮数据的数组2枚,记录速度计的数据的数组1枚.

其他的变量这篇文章用不到,就当他不存在了

-(void)updateXuanwheelSpeed:(NSTimeInterval)timeinterval ForPosition:(int)position
{
    switch (position) {
        case 0:
        {
            //########
            [xuanWheelTemporaryList addObject:@(timeinterval)];
            xuanwheelRevolution1++;
            //########
            break;
        }
        case 1:
        {
            //########
            [xuanWheelTemporaryList addObject:@(timeinterval)];
            xuanwheelRevolution2++;
            //########
            break;
        }
        default:
            break;
    }    
}

这是处理炫轮数据的方法,把一圈的时间间隔记录下来,分开写是因为要分别记录两个轮子转的圈数,帮助计算距离.

-(void)updateSpeeder:(Float32)frequency
{
    //########
    [publicTemporaryList addObject:@(frequency)];
}

公有协议的数据,我在这之前就一经计算好了转一圈的频率.已知在第X1秒转了Y1圈,在X2秒转了Y2圈.那么频率就是(X1-X2)/(Y1-Y2)

记录完毕数据,就该计算相关信息了

Calculate

-(void)optimizeSpeed
{
    if (######) {
        //....
    }else
    {
        //#####
        //Reason1:只有一组数据,还怎么计算平均速度;Reason2:#####
        if (publicTemporaryList.count>1||xuanWheelTemporaryList.count>1) {
            int count=0;
            //time用来存放这1秒内平均的转过1圈需要的时间
            NSTimeInterval time=0;
            for(NSNumber *item in publicTemporaryList)
            {
                //这里来计算公有协议的速度信息
                if (item.floatValue>5) {
                    //这么慢还是忽略吧
                    continue;
                }
                if (item.floatValue<=0) {
                    //谁解析的数据!!
                    continue;
                }
                time+=1.f/item.floatValue;
                count++;
            }
            if (publicTemporaryList.count>0) {
                if (publicTemporaryList.count>1) {
                    [publicTemporaryList removeObjectsInRange:NSMakeRange(0, publicTemporaryList.count-1)];
                }else
                {
                    [publicTemporaryList removeAllObjects];
                }
                //算完删光,留下一组数据,为下一秒做帮助,防止突变
            }
            for(NSNumber *item in xuanWheelTemporaryList)
            {
                //这里来计算炫轮的速度信息
                if (item.floatValue>5) {
                    continue;
                }
                if (item.floatValue<=0) {
                    continue;
                }
                time+=item.floatValue;
                count++;
            }
            if (xuanWheelTemporaryList.count>0) {
                if (xuanWheelTemporaryList.count>1) {
                    [xuanWheelTemporaryList removeObjectsInRange:NSMakeRange(0, xuanWheelTemporaryList.count-1)];
                }else
                {
                    [xuanWheelTemporaryList removeAllObjects];
                }
                //Again,留一组数据来备用
            }
            if (count==0||time==0) {
                currentS=0;
            }else
                currentS=[PublicResource sharedObject].roundLength*count/time*3600;
                //这个就是这1秒的速度了咯
        }else
        {
            //#####
        }
    }
//最新的最高速度
    if (currentS>max_inner_speed) {
        max_inner_speed=currentS;
    }
//防止速度突变
    if (currentS>lastSpeed+30) {
        lastSpeed=currentS;
        currentS=0;
    }
//强制限速
    if (currentS>80) {
        currentS=80;
    }
}

差不多,速度就是这么计算的,在实际测试中,感觉还是满准确地~

阅读剩下更多

编程

Python 处理字节流

python处理字节的点点滴滴

python有多爽,我就不说了,反正想得到的事情,基本都有办法用python来解决,当然还有一个很重要的优点: 写python不用编译~

Context

  1. 之前毕业设计做的是SMS4的实现与安全性分析.安全性分析是什么鬼?查了查书,好像是用有特征的原文来进行测试……,具体的我也忘记了.当时就琢磨,怎么样可以做一个酷炫的安全性分析呢?
  2. 公司的新产品是一款智能硬件,它会通过TCP给服务器发送指令(当然不是熟悉的HTTP请求,是传说中的字节流)

Requirement

  1. commands+re : 这个组合可以轻轻松松的提取出命令行程序的有用信息
  2. binascii : 这个东西可以轻松的产生各种各样和二进制有关或者和ascii有关的字符串
  3. struct : 超级牛逼的解包模块,瞬间把一个字节流解成元组

OK, Let’s go

commands+re

commands.output(cmd),cmd是shell命令我也不知道windows的命令可以不可以~~
这一条可以把运行结果返回出来(字符串形式)

然后用re来做正则匹配,再搭配一些split(),replace()之类的操作字符串的函数,就可以提取出一个命令行程序中有用的信息了.

binascii

binascii.b2a_hex(data)
binascii.hexlify(data)

data就是原始字符串,假设就是”abcde”
返回的也是字符串,不过是16进制的字符串”6162636465”

binascii.a2b_hex(hexstr)
binascii.unhexlify(hexstr)

这个函数就是反过来的
‘hexstr’是16进制的字符串,假定说是”6162636465”
返回的字符串就是他们所对应的ascii的字符,就是”abcde”

这有啥用嘞?

做测试!

让下位机发送一条指令可能还蛮复杂的,或者蛮浪费资源什么的,这样调试服务器代价会比较大.使用binascii来制作测试数据再好不过了.

假设下位机发送的数据是”0xaa,0xbb,0xcc,0x12,0x34,0xdd,0xee,0xff”

我们就可以模拟一条

a="aabbcc1234ddeeff"
b=asciibin.a2b_hex(a)

b就是模拟了服务器收到socket字节流.真方便

struct

收到字节流,根据协议,得知其中的8个字节是double,4个字节是float.

嗯,字节转整形还是有办法的,那么转成浮点型应该怎么转呢??

import struct吧

将python数据封装成字节流

struct.pack(format,argvs...)

将字节流解析成python数据类型

struct.unpack(format,string)

format很有意思,简单说就是解析规则.

比如解析成float,就写’f’

解析成double,就写成’d’

那么一个数据流里面既有float又有double怎么办呢?

嗯,截取字符串嘛!

struct.unpack('f',s1[0:4])
struct.unpack('d',s1[4:12])

哈哈,我一开始也是这样写的,后面章鱼兄跟我说,format里面可以写很多东西

struct.unpack('fd',s1)

这个会直接返回(r1,r2),其中r1就是那个float,r2就是那个double

真的好方便

附表一张

format 类型
f float
d double
c char
i int
l long
3s 长度为3的字符串
4s 长度为4的字符串
5s 长度为5的字符串

至于struct.pack()嘛,既然format都有了,那么应该不难理解了,在python里面试一试就明白了

阅读剩下更多

返回顶部