unraid环境下用koel搭建自己的家庭音乐库

koel是一个可以将本地音乐展示为在线web服务形式的php框架:https://koel.dev/

刚开始参考 https://post.smzdm.com/p/a3gv68v7/
发现pc上的web正常,但是手机端不行,因为版本不是最新的,初步猜疑是不是版本太旧的bug。

本来想自己弄一个最新版的docker版本,后面发现https://hub.docker.com 上有活跃的最新版本:https://hub.docker.com/r/hyzual/koel/

准备工作,设置自定义网络

为了确保宝塔的ip不会变动,保证数据库的稳定访问,我们需要创建自定义网络:

docker network create --subnet=172.18.0.0/16 myNetwork

创建后可以用 docker network ls来查看,如果已经创建,则进行下一步。

把baota加进自定义网络

docker network connect --ip 172.18.0.200 myNetwork baota

添加完成后可以用 docker inspect baota来确认,同时在另一个docker中来ping 172.18.0.200这个地址,确认是否能ping通。

安装

上面的网络确认好了后,就可以真正安装了:

docker run -d --name koel --link=baota -p xxx:80 --privileged=true --restart always -e DB_CONNECTION=mysql -e DB_HOST=baota -e DB_DATABASE=xxx -e DB_USERNAME=xxx -e DB_PASSWORD=xxx --network=myNetwork --ip 172.18.0.201 -v /mnt/user/media/music/songs:/music -v /mnt/user/media/music/covers:/var/www/html/public/img/covers -v /mnt/user/media/music/search_indexs:/var/www/html/storage/search-indexes hyzual/koel

这里连接了第三方的docker,用于数据库支持。因为我自己的是用的宝塔里面的mysql数据库,所以里面设置了相关的参数:
--link=baota

数据库访问参数:
DB_CONNECTION=mysql -e DB_HOST=baota -e DB_DATABASE=xxx -e DB_USERNAME=xxx -e DB_PASSWORD=xxx

网络设置,保证和数据库docker使用共同的自定义网络:
--network=myNetwork --ip 172.18.0.201

也可以用官方的docker镜像,经过测试也是可以正常:

docker run -d --name koel \
    -p xxx:80 \
    -e DB_CONNECTION=mysql \
    -e DB_HOST=baota \
    -e DB_DATABASE=koel \
    -e DB_USERNAME=koel \
    -e DB_PASSWORD=xxx \
    --network=myNetwork  \
    --ip 172.18.0.201 \
    -v /mnt/user/media/music/songs:/music \
    -v  /mnt/user/media/music/covers:/var/www/html/public/img/covers \
    -v /mnt/user/media/music/search_indexs:/var/www/html/storage/search-indexes \
    phanan/koel

安装后需要进入docker内部,执行初始账号初始化:

#php artisan koel:init --no-assets
Attempting to install or upgrade Koel.
Remember, you can always install/upgrade manually following the guide here:
📙  https://docs.koel.dev

App key exists -- skipping
Migrating database
Creating default admin account
Seeding initial data
The absolute path to your media directory. If this is skipped (left blank) now, you can set it later via the web interface.

 Media path [/music]:
 > 

🎆  Success! Koel can now be run from localhost with `php artisan serve`.
Log in with email xxx@koel.dev and password xxxxxx
You can also scan for media with `php artisan koel:sync`.
Again, visit 📙 https://docs.koel.dev for the official documentation.
Feeling generous and want to support Koel's development? Check out https://github.com/users/phanan/sponsorship 🤗
Thanks for using Koel. You rock! 🤘

安装完成后,按照设定的端口去访问,出现熟悉的首页。

默认账户:admin@koel.dev
默认密码:KoelIsCool

安装要点

  1. 需要的keol数据库,访问权限刚开始要设置成“所有人”。等安装成功了,再将访问权限设置成“指定IP”,比如示例docker对应的ip 172.18.0.201

版本更新

默认unraid界面的更新链接是无法更新的,会提示:

Configuration not found. Was this container created using this plugin?

解决的办法就是把image删掉,重新run。因为你的数据都在数据库,所以更新后,不会影响数据。

源码安装gcc9.3.0

环境

centos7

准备工作

yum install zstd

yum install glibc-headers
yum install gcc-c++

预下载

https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-9.3.0/gcc-9.3.0.tar.gz

解压后进入目录,启动预下载,可能会很慢,或者卡死:
./contrib/download_prerequisites

后面发现下载bz2文件都提示无法解压,可能是工具环境的问题,也可能是下载完整性问题。

于是改为手动下载最新版:

https://mirrors.sjtug.sjtu.edu.cn/gnu/gmp/gmp-6.2.1.tar.zst
https://www.mpfr.org/mpfr-current/mpfr-4.1.0.tar.gz
https://mirrors.sjtug.sjtu.edu.cn/gnu/mpc/mpc-1.2.1.tar.gz
http://isl.gforge.inria.fr/isl-0.23.tar.gz

zst文件解压: tar -I zstd -vxf xxxx

改名

解压后,都改成不带后缀版本的文件名,比如 gmp mpfr

编译

./configure --prefix=/usr/local/gcc --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release  --enable-plugin --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux --disable-multilib

make

目标文件替换

编译完成后,所有目标文件都是在prefix路径下,但是系统环境可能还是旧的。这时需要升级替换。
主要有:

bin => /usr/bin
lib => /usr/lib/gcc/x86_64-redhat-linux/9.3.0/
lib64 => /usr/lib64
libexec => /usr/libexec

玩转NAS之搞清楚端口映射

玩NAS的因为解决外网访问的问题,经常需要将外网映射成内网。

如果你用的是unraid, 那么大部分的软件都是通过docker的方式安装的,也不可避免的要映射端口,将本地局域网端口映射成docker 内部应用的服务端口。

这里就有3层端口:外网->内部局域网->docker内部
两层映射:一次在路由器上映射,一次在启动docker时映射。

搞清楚了这3层端口两层映射行为,那么大部分的网络问题就都能迎刃而解。

宝塔面板中phpmyadmin404问题

宝塔是一套web面板,类似于webmin。

我们在unraid中安装后,一般都会套件安装 nginx,php,mysql,phpmyadmin等。

使用中在mysql页面点击phpMyAdmin按钮时,提示404:
http://192.168.x.x:123/phpmyadmin_94fece33f6f785c2/index.php

然后登录进baota daocker里面,确认了下nginx的配置文件。找到了nginx 123 server的root,发现是对应的路径问题。

解决

既然定位了原因,那么解决就很简单了,对症下药,通过ln -s 源 目的 创建目录的软连接。

OK...

enable clang-format in VSC

之前的环境都是好用的,一次深度垃圾清理后,发现所有配置和clang-format都不好用了。

clang-format要起作用:

  1. 装clang-format插件
  2. 装clang-format.exe,这个需要安装LLVM或者 cpptools插件
  3. 正确的配置文件

安装

  1. 安装clang-format 插件。xaver出品那个
  2. 配置Clang-format: Executable 有效路径。

配置

网上配置很多,只列举几个比较重要的:

配置vsc用的格式化工具为clang-foramt

  • Default Formatter(editor.defaultFormatter)

clang-format.exe路径配置

Clang-format:Executable(clang-format.executable)

保存文件自动格式化文件

  • format on save(editor.formatOnSave):yes
  • Format On Save Mode:file(默认)

格式的控制文件

方式1: 填写file。表示在项目根目录下搜索.clang-format
方式2: 修改VSC的Clang_format_style属性,直接填入键值对,也就是.clang-format的有效内容,这样就相当于全局配置文件了:

BasedOnStyle: LLVM
BreakBeforeBraces: Linux
Language: Cpp
ColumnLimit: 120
ReflowComments: false
SortIncludes: false
BreakBeforeBraces: Custom
BraceWrapping:
  AfterEnum: false
  AfterStruct: false
  SplitEmptyFunction: false
AlignConsecutiveAssignments: true
AlignConsecutiveMacros: true
AllowShortFunctionsOnASingleLine: None

windows touchpad driver

型号:151SK lenovo

最近深度清理后,发现鼠标的控制面板管理touchpad标签页不见了,touchpad无法禁用,一直在打字过程中光标乱飞。

找了lenovo的151sk的驱动页面,找了对应的touchpad驱动,不好用。

这个是显示win10的,也不是针对151sk的,但是实际测试可用:
ce0j06af.exe

通过nginx无插件方式实现git hook的方式

git hook 是一种很有用的方式。

一般的实现就是在本地开启一个监听hook url的http server,然后对url进行处理。

因为很多服务器都有nginx作为web server,所以我们优先考虑如何利用nginx。

一般网上比较多的推荐是用nginx的衍生版本openresty,通过lua来实现。

这里记录一个简单的思路:
通过nginx的lacation来匹配hook url特征,然后产生特定的access 或者error日志,然后通过脚本监控日志的变化,来判断是否有hook消息触发。这样nginx是现成的,且实现方法是无阻塞的,只要实现个一直监控文件大小的脚本就可以了,非常简单。

nginx部分简单配置

location /xxx/xxx/x {
        return 200 "xxx";
        access_log  /xxx.log;
    }

这里需要确认hook的触发是成功access还是失败error,才能正确的触发日志文件.

监控文件大小的脚本

这个脚本负责日志变化时,执行我们的git hook动作。

#!/bin/sh

# 要监控的文件
mfile=/xxx.log

#启动一个循环,定时检查文件是否存在
while true; do
    #监控文件大小
    fsize=`stat -c "%s" $mfile`
    if [ $fsize !=  1  ]; then
        echo "">$mfile
        echo "log file update"
        #TODO:需要的逻辑放这里
        ...
    fi
    sleep 5
done

监控脚本开机启动

脚本好了,然后就是要设置开机启动,否则服务器重启后,hook就失效了。
开机启动,有很多办法设置,比如最简单的rc.local脚本

Linux errno详解

存档文章。原始链接:https://www.cnblogs.com/Jimmy1988/p/7485133.html

Linux errno

  1. 错误码 / errno
    Linux中系统调用的错误都存储于 errno中,errno由操作系统维护,存储就近发生的错误,即下一次的错误码会覆盖掉上一次的错误。

PS: 只有当系统调用或者调用lib函数时出错,才会置位errno!

查看系统中所有的errno所代表的含义,可以采用如下的代码:

/* Function: obtain the errno string
*   char *strerror(int errno)
*/

#include <stdio.h>
#include <string.h>     //for strerror()
//#include <errno.h>
int main()
{
    int tmp = 0;
    for(tmp = 0; tmp <=256; tmp++)
    {
        printf("errno: %2d\t%s\n",tmp,strerror(tmp));
    }
    return 0;
}

输出信息如下:

errno:  0       Success
errno:  1       Operation not permitted
errno:  2       No such file or directory
errno:  3       No such process
errno:  4       Interrupted system call
errno:  5       Input/output error
errno:  6       No such device or address
errno:  7       Argument list too long
errno:  8       Exec format error
errno:  9       Bad file descriptor
errno: 10       No child processes
errno: 11       Resource temporarily unavailable
errno: 12       Cannot allocate memory
errno: 13       Permission denied
errno: 14       Bad address
errno: 15       Block device required
errno: 16       Device or resource busy
errno: 17       File exists
errno: 18       Invalid cross-device link
errno: 19       No such device
errno: 20       Not a directory
errno: 21       Is a directory
errno: 22       Invalid argument
errno: 23       Too many open files in system
errno: 24       Too many open files
errno: 25       Inappropriate ioctl for device
errno: 26       Text file busy
errno: 27       File too large
errno: 28       No space left on device
errno: 29       Illegal seek
errno: 30       Read-only file system
errno: 31       Too many links
errno: 32       Broken pipe
errno: 33       Numerical argument out of domain
errno: 34       Numerical result out of range
errno: 35       Resource deadlock avoided
errno: 36       File name too long
errno: 37       No locks available
errno: 38       Function not implemented
errno: 39       Directory not empty
errno: 40       Too many levels of symbolic links
errno: 41       Unknown error 41
errno: 42       No message of desired type
errno: 43       Identifier removed
errno: 44       Channel number out of range
errno: 45       Level 2 not synchronized
errno: 46       Level 3 halted
errno: 47       Level 3 reset
errno: 48       Link number out of range
errno: 49       Protocol driver not attached
errno: 50       No CSI structure available
errno: 51       Level 2 halted
errno: 52       Invalid exchange
errno: 53       Invalid request descriptor
errno: 54       Exchange full
errno: 55       No anode
errno: 56       Invalid request code
errno: 57       Invalid slot
errno: 58       Unknown error 58
errno: 59       Bad font file format
errno: 60       Device not a stream
errno: 61       No data available
errno: 62       Timer expired
errno: 63       Out of streams resources
errno: 64       Machine is not on the network
errno: 65       Package not installed
errno: 66       Object is remote
errno: 67       Link has been severed
errno: 68       Advertise error
errno: 69       Srmount error
errno: 70       Communication error on send
errno: 71       Protocol error
errno: 72       Multihop attempted
errno: 73       RFS specific error
errno: 74       Bad message
errno: 75       Value too large for defined data type
errno: 76       Name not unique on network
errno: 77       File descriptor in bad state
errno: 78       Remote address changed
errno: 79       Can not access a needed shared library
errno: 80       Accessing a corrupted shared library
errno: 81       .lib section in a.out corrupted
errno: 82       Attempting to link in too many shared libraries
errno: 83       Cannot exec a shared library directly
errno: 84       Invalid or incomplete multibyte or wide character
errno: 85       Interrupted system call should be restarted
errno: 86       Streams pipe error
errno: 87       Too many users
errno: 88       Socket operation on non-socket
errno: 89       Destination address required
errno: 90       Message too long
errno: 91       Protocol wrong type for socket
errno: 92       Protocol not available
errno: 93       Protocol not supported
errno: 94       Socket type not supported
errno: 95       Operation not supported
errno: 96       Protocol family not supported
errno: 97       Address family not supported by protocol
errno: 98       Address already in use
errno: 99       Cannot assign requested address
errno: 100      Network is down
errno: 101      Network is unreachable
errno: 102      Network dropped connection on reset
errno: 103      Software caused connection abort
errno: 104      Connection reset by peer
errno: 105      No buffer space available
errno: 106      Transport endpoint is already connected
errno: 107      Transport endpoint is not connected
errno: 108      Cannot send after transport endpoint shutdown
errno: 109      Too many references: cannot splice
errno: 110      Connection timed out
errno: 111      Connection refused
errno: 112      Host is down
errno: 113      No route to host
errno: 114      Operation already in progress
errno: 115      Operation now in progress
errno: 116      Stale file handle
errno: 117      Structure needs cleaning
errno: 118      Not a XENIX named type file
errno: 119      No XENIX semaphores available
errno: 120      Is a named type file
errno: 121      Remote I/O error
errno: 122      Disk quota exceeded
errno: 123      No medium found
errno: 124      Wrong medium type
errno: 125      Operation canceled
errno: 126      Required key not available
errno: 127      Key has expired
errno: 128      Key has been revoked
errno: 129      Key was rejected by service
errno: 130      Owner died
errno: 131      State not recoverable
errno: 132      Operation not possible due to RF-kill
errno: 133      Memory page has hardware error
errno: 134~255  unknown error!

Linux中,在头文件 /usr/include/asm-generic/errno-base.h 对基础常用errno进行了宏定义:

#ifndef _ASM_GENERIC_ERRNO_BASE_H
#define _ASM_GENERIC_ERRNO_BASE_H

#define EPERM        1  /* Operation not permitted */
#define ENOENT       2  /* No such file or directory */
#define ESRCH        3  /* No such process */
#define EINTR        4  /* Interrupted system call */
#define EIO      5  /* I/O error */
#define ENXIO        6  /* No such device or address */
#define E2BIG        7  /* Argument list too long */
#define ENOEXEC      8  /* Exec format error */
#define EBADF        9  /* Bad file number */
#define ECHILD      10  /* No child processes */
#define EAGAIN      11  /* Try again */
#define ENOMEM      12  /* Out of memory */
#define EACCES      13  /* Permission denied */
#define EFAULT      14  /* Bad address */
#define ENOTBLK     15  /* Block device required */
#define EBUSY       16  /* Device or resource busy */
#define EEXIST      17  /* File exists */
#define EXDEV       18  /* Cross-device link */
#define ENODEV      19  /* No such device */
#define ENOTDIR     20  /* Not a directory */
#define EISDIR      21  /* Is a directory */
#define EINVAL      22  /* Invalid argument */
#define ENFILE      23  /* File table overflow */
#define EMFILE      24  /* Too many open files */
#define ENOTTY      25  /* Not a typewriter */
#define ETXTBSY     26  /* Text file busy */
#define EFBIG       27  /* File too large */
#define ENOSPC      28  /* No space left on device */
#define ESPIPE      29  /* Illegal seek */
#define EROFS       30  /* Read-only file system */
#define EMLINK      31  /* Too many links */
#define EPIPE       32  /* Broken pipe */
#define EDOM        33  /* Math argument out of domain of func */
#define ERANGE      34  /* Math result not representable */

#endif

在 /usr/include/asm-asm-generic/errno.h 中,对剩余的errno做了宏定义:

#ifndef _ASM_GENERIC_ERRNO_H
#define _ASM_GENERIC_ERRNO_H

#include <asm-generic/errno-base.h>

#define EDEADLK     35  /* Resource deadlock would occur */
#define ENAMETOOLONG    36  /* File name too long */
#define ENOLCK      37  /* No record locks available */
#define ENOSYS      38  /* Function not implemented */
#define ENOTEMPTY   39  /* Directory not empty */
#define ELOOP       40  /* Too many symbolic links encountered */
#define EWOULDBLOCK EAGAIN  /* Operation would block */
#define ENOMSG      42  /* No message of desired type */
#define EIDRM       43  /* Identifier removed */
#define ECHRNG      44  /* Channel number out of range */
#define EL2NSYNC    45  /* Level 2 not synchronized */
#define EL3HLT      46  /* Level 3 halted */
#define EL3RST      47  /* Level 3 reset */
#define ELNRNG      48  /* Link number out of range */
#define EUNATCH     49  /* Protocol driver not attached */
#define ENOCSI      50  /* No CSI structure available */
#define EL2HLT      51  /* Level 2 halted */
#define EBADE       52  /* Invalid exchange */
#define EBADR       53  /* Invalid request descriptor */
#define EXFULL      54  /* Exchange full */
#define ENOANO      55  /* No anode */
#define EBADRQC     56  /* Invalid request code */
#define EBADSLT     57  /* Invalid slot */

#define EDEADLOCK   EDEADLK

#define EBFONT      59  /* Bad font file format */
#define ENOSTR      60  /* Device not a stream */
#define ENODATA     61  /* No data available */
#define ETIME       62  /* Timer expired */
#define ENOSR       63  /* Out of streams resources */
#define ENONET      64  /* Machine is not on the network */
#define ENOPKG      65  /* Package not installed */
#define EREMOTE     66  /* Object is remote */
#define ENOLINK     67  /* Link has been severed */
#define EADV        68  /* Advertise error */
#define ESRMNT      69  /* Srmount error */
#define ECOMM       70  /* Communication error on send */
#define EPROTO      71  /* Protocol error */
#define EMULTIHOP   72  /* Multihop attempted */
#define EDOTDOT     73  /* RFS specific error */
#define EBADMSG     74  /* Not a data message */
#define EOVERFLOW   75  /* Value too large for defined data type */
#define ENOTUNIQ    76  /* Name not unique on network */
#define EBADFD      77  /* File descriptor in bad state */
#define EREMCHG     78  /* Remote address changed */
#define ELIBACC     79  /* Can not access a needed shared library */
#define ELIBBAD     80  /* Accessing a corrupted shared library */
#define ELIBSCN     81  /* .lib section in a.out corrupted */
#define ELIBMAX     82  /* Attempting to link in too many shared libraries */

#define ELIBEXEC    83  /* Cannot exec a shared library directly */
#define EILSEQ      84  /* Illegal byte sequence */
#define ERESTART    85  /* Interrupted system call should be restarted */
#define ESTRPIPE    86  /* Streams pipe error */
#define EUSERS      87  /* Too many users */
#define ENOTSOCK    88  /* Socket operation on non-socket */
#define EDESTADDRREQ    89  /* Destination address required */
#define EMSGSIZE    90  /* Message too long */
#define EPROTOTYPE  91  /* Protocol wrong type for socket */
#define ENOPROTOOPT 92  /* Protocol not available */
#define EPROTONOSUPPORT 93  /* Protocol not supported */
#define ESOCKTNOSUPPORT 94  /* Socket type not supported */
#define EOPNOTSUPP  95  /* Operation not supported on transport endpoint */
#define EPFNOSUPPORT    96  /* Protocol family not supported */
#define EAFNOSUPPORT    97  /* Address family not supported by protocol */
#define EADDRINUSE  98  /* Address already in use */
#define EADDRNOTAVAIL   99  /* Cannot assign requested address */
#define ENETDOWN    100 /* Network is down */
#define ENETUNREACH 101 /* Network is unreachable */
#define ENETRESET   102 /* Network dropped connection because of reset */
#define ECONNABORTED    103 /* Software caused connection abort */
#define ECONNRESET  104 /* Connection reset by peer */
#define ENOBUFS     105 /* No buffer space available */
#define EISCONN     106 /* Transport endpoint is already connected */
#define ENOTCONN    107 /* Transport endpoint is not connected */
#define ESHUTDOWN   108 /* Cannot send after transport endpoint shutdown */
#define ETOOMANYREFS    109 /* Too many references: cannot splice */
#define ETIMEDOUT   110 /* Connection timed out */
#define ECONNREFUSED    111 /* Connection refused */
#define EHOSTDOWN   112 /* Host is down */
#define EHOSTUNREACH    113 /* No route to host */
#define EALREADY    114 /* Operation already in progress */
#define EINPROGRESS 115 /* Operation now in progress */
#define ESTALE      116 /* Stale file handle */
#define EUCLEAN     117 /* Structure needs cleaning */
#define ENOTNAM     118 /* Not a XENIX named type file */
#define ENAVAIL     119 /* No XENIX semaphores available */
#define EISNAM      120 /* Is a named type file */
#define EREMOTEIO   121 /* Remote I/O error */
#define EDQUOT      122 /* Quota exceeded */

#define ENOMEDIUM   123 /* No medium found */
#define EMEDIUMTYPE 124 /* Wrong medium type */
#define ECANCELED   125 /* Operation Canceled */
#define ENOKEY      126 /* Required key not available */
#define EKEYEXPIRED 127 /* Key has expired */
#define EKEYREVOKED 128 /* Key has been revoked */
#define EKEYREJECTED    129 /* Key was rejected by service */

/* for robust mutexes */
#define EOWNERDEAD  130 /* Owner died */
#define ENOTRECOVERABLE 131 /* State not recoverable */

#define ERFKILL     132 /* Operation not possible due to RF-kill */

#define EHWPOISON   133 /* Memory page has hardware error */

#endif
  1. 打印错误信息
    1). 打印错误信息 / perror
    作用:
    打印系统错误信息

头文件:

include

函数原型:

void perror(const char *s)
参数:

s: 字符串提示符

输出形式:
const char *s: strerror(errno) //提示符:发生系统错误的原因
返回值:
无返回值
2). 字符串显示错误信息 / strerror
作用:
将错误码以字符串的信息显示出来

头文件:

include

函数原型:

char *strerror(int errnum);
参数:

errnum: 即errno

返回值:
返回错误码字符串信息

eclipse console build日志乱码

现象

windows中通过免安装eclipse工具包,编译时部分日志:

make[1]: 杩涘叆鐩綍鈥?/home/w600/project/w600_sdk/app鈥?
make[1]: 绂诲紑鐩綍鈥?/home/w600/project/w600_sdk/app鈥?
make[1]: 杩涘叆鐩綍鈥?/home/w600/project/w600_sdk/demo鈥?
make[2]: 杩涘叆鐩綍鈥?/home/w600/project/w600_sdk/demo/console鈥?
make[2]: 绂诲紑鐩綍鈥?/home/w600/project/w600_sdk/demo/console鈥?
make[1]: 绂诲紑鐩綍鈥?/home/w600/project/w600_sdk/demo鈥?
make[1]: 杩涘叆鐩綍鈥?/home/w600/project/w600_sdk/platform/boot/gcc鈥?
make[1]: 绂诲紑鐩綍鈥?/home/w600/project/w600_sdk/platform/boot/gcc鈥?

定位

首先根据make[1]在全工程搜索,根据搜出的内容对比,大概知道乱码的部分内容是“进入目录”和“离开目录”, 尾部的是中文的双引号。所以基本可以知道内容是对的,只是UTF8的中文支持问题了。

解决

  • 网上的方案1:在eclipse中修改相关的配置,修改为UTF8,无效。
  • 网上的方案2:在eclipse.ini 中增加-Dfile_encoding=UTF-8。这个思路应该时对了,但是在我本地无法生效,应该是eclipse目录中的eclipse.ini没生效,具体原因,没有细究。
  • 可用的方案3:最终的方案就是在windows系统环境变量中增加:JAVA_TOOL_OPTIONS,值为:-Dfile_encoding=UTF-8。由此联想 通过启动命令脚本带参数的方式应该也可以。

CD walkman D-EJ785

淘了个D-EJ785,来听一些买来的唱片CD。

关机

没有关机键,按下停止键(实心方块)后,就相当于关机了,可以进行CD或者电池操作。

G-PROTECTION

G-PROTECTION:防止声音跳动,这个一般是机器震动后者光盘划伤造成。

AVLS

AVLS:AVLS即自动音量限制系统的简称。它的作用就是:控制音量,避免用户在使用耳机收听音乐时因音量过大而导致听力受损。设定为limit时,保护听力,限制最大音量。

MODE

1:单首播放
SHUF:随机播放
PGM:按照喜欢顺序播放

SOUND

BASS1/2:加强的低音模式,BASS2比BASS1低音强

IOS OC NSUserDefaults

NSUserDefaults

NSUserDefaults可以理解为本地的key-value数据库,类似sqlite的本地数据库的作用。

获取key的值

例子:

[[NSUserDefaults standardUserDefaults] boolForKey:kWifiOptimizeSwitch];

api:

/// -stringForKey: is equivalent to -objectForKey:, except that it will convert NSNumber values to their NSString representation. If a non-string non-number value is found, nil will be returned.
- (nullable NSString *)stringForKey:(NSString *)defaultName;

/// -arrayForKey: is equivalent to -objectForKey:, except that it will return nil if the value is not an NSArray.
- (nullable NSArray *)arrayForKey:(NSString *)defaultName;
/// -dictionaryForKey: is equivalent to -objectForKey:, except that it will return nil if the value is not an NSDictionary.
- (nullable NSDictionary<NSString *, id> *)dictionaryForKey:(NSString *)defaultName;
/// -dataForKey: is equivalent to -objectForKey:, except that it will return nil if the value is not an NSData.
- (nullable NSData *)dataForKey:(NSString *)defaultName;
/// -stringForKey: is equivalent to -objectForKey:, except that it will return nil if the value is not an NSArray<NSString *>. Note that unlike -stringForKey:, NSNumbers are not converted to NSStrings.
- (nullable NSArray<NSString *> *)stringArrayForKey:(NSString *)defaultName;
/*!
 -integerForKey: is equivalent to -objectForKey:, except that it converts the returned value to an NSInteger. If the value is an NSNumber, the result of -integerValue will be returned. If the value is an NSString, it will be converted to NSInteger if possible. If the value is a boolean, it will be converted to either 1 for YES or 0 for NO. If the value is absent or can't be converted to an integer, 0 will be returned.
 */
- (NSInteger)integerForKey:(NSString *)defaultName;
/// -floatForKey: is similar to -integerForKey:, except that it returns a float, and boolean values will not be converted.
- (float)floatForKey:(NSString *)defaultName;
/// -doubleForKey: is similar to -integerForKey:, except that it returns a double, and boolean values will not be converted.
- (double)doubleForKey:(NSString *)defaultName;
/*!
 -boolForKey: is equivalent to -objectForKey:, except that it converts the returned value to a BOOL. If the value is an NSNumber, NO will be returned if the value is 0, YES otherwise. If the value is an NSString, values of "YES" or "1" will return YES, and values of "NO", "0", or any other string will return NO. If the value is absent or can't be converted to a BOOL, NO will be returned.

 */
- (BOOL)boolForKey:(NSString *)defaultName;
/*!
 -URLForKey: is equivalent to -objectForKey: except that it converts the returned value to an NSURL. If the value is an NSString path, then it will construct a file URL to that path. If the value is an archived URL from -setURL:forKey: it will be unarchived. If the value is absent or can't be converted to an NSURL, nil will be returned.
 */
- (nullable NSURL *)URLForKey:(NSString *)defaultName API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));

看api,可以看到支持多种数据类型的获取

设置key的值

例子:

NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    [userDefault setBool:senderSwitch.on forKey:kWifiOptimizeSwitch];

api:

/// -setInteger:forKey: is equivalent to -setObject:forKey: except that the value is converted from an NSInteger to an NSNumber.
- (void)setInteger:(NSInteger)value forKey:(NSString *)defaultName;
/// -setFloat:forKey: is equivalent to -setObject:forKey: except that the value is converted from a float to an NSNumber.
- (void)setFloat:(float)value forKey:(NSString *)defaultName;
/// -setDouble:forKey: is equivalent to -setObject:forKey: except that the value is converted from a double to an NSNumber.
- (void)setDouble:(double)value forKey:(NSString *)defaultName;
/// -setBool:forKey: is equivalent to -setObject:forKey: except that the value is converted from a BOOL to an NSNumber.
- (void)setBool:(BOOL)value forKey:(NSString *)defaultName;
/// -setURL:forKey is equivalent to -setObject:forKey: except that the value is archived to an NSData. Use -URLForKey: to retrieve values set this way.
- (void)setURL:(nullable NSURL *)url forKey:(NSString *)defaultName API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));

同样支持各种数据的set。

说明:在set了后,数据其实还在内存数据结构中。还没有持久化到本地存储。

其他说明

 -synchronize is deprecated and will be marked with the API_DEPRECATED macro in a future release.

 -synchronize blocks the calling thread until all in-progress set operations have completed. This is no longer necessary. Replacements for previous uses of -synchronize depend on what the intent of calling synchronize was. If you synchronized...
 - ...before reading in order to fetch updated values: remove the synchronize call
 - ...after writing in order to notify another program to read: the other program can use KVO to observe the default without needing to notify
 - ...before exiting in a non-app (command line tool, agent, or daemon) process: call CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication)
 - ...for any other reason: remove the synchronize call

IOS OC UITableView

object c 中的UITableView 可以实现灵活的列表内容效果。列表的内容可以是多种类型元素。

主要步骤

  1. 正确实现numberOfSectionsInTableView和numberOfRowsInSection,获取正确的sections和每个section中的rows。例如:
- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView {
    NSInteger num =[self.settingCellTitles count]+1;
    return num;
}

- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
    if (section == 0) {
        return 1;
    } else {
        NSInteger num =[[self.settingCellTitles objectAtIndex:section-1] count];
        return num;
    }
}
  1. 实现 UITableViewCell,可以理解为每个row中的内容。
  2. 实现didSelectRowAtIndexPath,点击每个row的逻辑

要点

因为可以是一个section多个rows,也可能是多个sections,相当于一维数组和二维数组的区别。

对于一维数组获取row index:

 [[_settingCellModes objectAtIndex:indexPath.row] integerValue];

对于二维数组:

[[[self.settingCellModes objectAtIndex:indexPath.section-1] objectAtIndex:indexPath.row] integerValue];

主要就是先获取indexPath.section对应的section,而一维是直接
indexPath.row。

联盛德W600 W100 wifi scan结果的串口报文解析

目前使用的版本,w100出厂自带wifi scan逻辑代码,上电开机后会从spi和uart发出wifi scan的结果,开机后只输出一次。

uart输出的报文如下:

报文样例

hspi tx:449
Z0C:4B:54:08:44:85,jinkun-lab2,-36|D0:C7:C0:6F:6D:96,COREACH_OFFICE,-47|88:44:77:F2:D2:74,dcup,-59|54:BA:D6:33:BB:3C,2305,-59|D0:76:E7:95:2D:F4,AIT-OFFICE,-59|40:F4:20:E4:CF:22,ChinaNet-vCCG,-64|7C:B5:9B:6B:A4:9A,TP-FX-2305-2.4G,-64|68:13:24:66:65:AC,2306,-71|64:6E:97:B7:99:DA,FX-2302,-71|F0:92:B4:D6:9A:29,ChinaNet-gSHU,-71|64:6E:97:5A:EC:42,涓滆揪閫氭櫤鑳借惀閿€涓績,-71|F0:92:B4:9C:51:19,ChinaNet-Dxat,-82|B0:7F:B9:94:25:8B,pinballtest,-90|

对应的hex:

68 73 70 69 20 74 78 3A 34 34 39 0D 0A 5A 0D 30 43 3A 34 42 3A 35 34 3A 30 38 3A 34 34 3A 38 35 2C 6A 69 6E 6B 75 6E 2D 6C 61 62 32 2C 2D 33 36 7C 44 30 3A 43 37 3A 43 30 3A 36 46 3A 36 44 3A 39 36 2C 43 4F 52 45 41 43 48 5F 4F 46 46 49 43 45 2C 2D 34 37 7C 38 38 3A 34 34 3A 37 37 3A 46 32 3A 44 32 3A 37 34 2C 64 63 75 70 2C 2D 35 39 7C 35 34 3A 42 41 3A 44 36 3A 33 33 3A 42 42 3A 33 43 2C 32 33 30 35 2C 2D 35 39 7C 44 30 3A 37 36 3A 45 37 3A 39 35 3A 32 44 3A 46 34 2C 41 49 54 2D 4F 46 46 49 43 45 2C 2D 35 39 7C 34 30 3A 46 34 3A 32 30 3A 45 34 3A 43 46 3A 32 32 2C 43 68 69 6E 61 4E 65 74 2D 76 43 43 47 2C 2D 36 34 7C 37 43 3A 42 35 3A 39 42 3A 36 42 3A 41 34 3A 39 41 2C 54 50 2D 46 58 2D 32 33 30 35 2D 32 2E 34 47 2C 2D 36 34 7C 36 38 3A 31 33 3A 32 34 3A 36 36 3A 36 35 3A 41 43 2C 32 33 30 36 2C 2D 37 31 7C 36 34 3A 36 45 3A 39 37 3A 42 37 3A 39 39 3A 44 41 2C 46 58 2D 32 33 30 32 2C 2D 37 31 7C 46 30 3A 39 32 3A 42 34 3A 44 36 3A 39 41 3A 32 39 2C 43 68 69 6E 61 4E 65 74 2D 67 53 48 55 2C 2D 37 31 7C 36 34 3A 36 45 3A 39 37 3A 35 41 3A 45 43 3A 34 32 2C E4 B8 9C E8 BE BE E9 80 9A E6 99 BA E8 83 BD E8 90 A5 E9 94 80 E4 B8 AD E5 BF 83 2C 2D 37 31 7C 46 30 3A 39 32 3A 42 34 3A 39 43 3A 35 31 3A 31 39 2C 43 68 69 6E 61 4E 65 74 2D 44 78 61 74 2C 2D 38 32 7C 42 30 3A 37 46 3A 42 39 3A 39 34 3A 32 35 3A 38 42 2C 70 69 6E 62 61 6C 6C 74 65 73 74 2C 2D 39 30 7C 0A 

字段说明

格式说明:

hspi tx:[字节数] 0x0d 0x0a
0x5A [0xNN][wifi scan list] 0x0a

字节数:回车换后后面的内容长度,但是实际计算下来发现是 字节数-1。
0x5A:是二进制报文头。
0xNN:标识wifi的个数。
wifi scan list:wifi scan list由多个类似的wifi节点:

40:16:9F:33:6A:18,COREACH,-82|

拼接而成。

解析

知道格式了,解析就很简单了。

wordpress更新php7.4后mysql连接问题

问题

Warning: mysqli_connect(): (HY000/2002): No such file or directory

更新完php7.4 后,再访问wordpress后,出现了上述的错误(开启DEBUG)。

定位

  1. 先单独通过mysql 命令在本地测试确认mysql服务正常。
  2. 检查wp-config.php的配置,没有问题
  3. 检查php的mysql支持,通过phpinfo()确认mysql支持没问题
  4. 初步锁定在php访问mysql的这个过程。

mysql status

登录mysql 后,通过status命令产看mysql socket:

UNIX socket:        /tmp/mysql.sock

php中mysql的配置

  1. 确认php.ini的配置文件路径:
# php --ini
Configuration File (php.ini) Path: /etc
Loaded Configuration File:         /etc/php.ini
  1. 通过phpinfo()页面来确认mysql的socket配置:
pdo_mysql.default_socket
mysqli.default_socket

解决

通过上面的过程,发现phpinfo页面和mysql实际的socket文件不一致,于是修改php.ini:

pdo_mysql.default_socket=/tmp/mysql.sock
mysqli.default_socket=/tmp/mysql.sock

或者修改my.cnf中相关:

[client]
socket=/tmp/mysql.sock
[mysqld]
socket=/tmp/mysql.sock

php和wordpress中版本显示不一致问题

现象

更新php到7.4后,在wordpress面板仍然提示php版本为7.1,通过或 php -v命令确认7.4更新成功,让人疑惑。

分析

  1. 既然php -v没有问题,那么就是web server相关使用的版本没更新,web server部分我们使用的是php-fpm。
  2. 搜索php-fpm,找到相关执行文件,确认php-fpm -v,确认是7.1。

解决

既然确认是php-fpm的问题,最简单的就是通过yum来更新php对应的php-fpm,更新后在检查,确认版本已经变成7.4。

DirectoryLister文件列表展示页面生成工具介绍

需求

寻找一款简单部署的工具,可以将服务上的文件通过页面列表展示,从而方面下载,有其他功能更好。

DirectoryLister

一款开源,开箱即用的好工具,github repo:
https://github.com/DirectoryLister/DirectoryLister

主要特点:

  1. php语言。
  2. 支持搜索。
  3. 其他见repo。

安装事项

  1. php 版本要大于>=7.2。
  2. 组件要求 zip,dom,fileinfo。
  3. cache目录要有写入权限,否则无法正常展示页面,需要通过php-fpm错误日志来定位。

使用

  1. 配置对应的php支持,一般就是设置nginx或者php-fpm相关fastcgi配置项。
  2. 需要安装在域名的根目录,在子目录会有问题。
  3. Copy .env.example to .env。
  4. Edit the configuration values in .env。
  5. 放在根目录后,可能会暴露不想暴露的文件和目录,在根目录创建 .hidden文件,配置语法和git的ignore文件一样,就可以实现相关文件目录的隐藏。

done!

wordpress 插件异常导致无法登录

问题

出现错误时,无法通过admin页面登录到后台面板,而且页面的错误提示也有限,无法确认具体问题。这个时候,只能通过ssh来尝试解决。

解决

  1. 开启wordpress 调试:
    https://wordpress.org/support/article/debugging-in-wordpress/
    只需修改 wp-config.php中下面的开关就可以在页面看到进一步的调试信息:

    // Enable WP_DEBUG mode
    define( 'WP_DEBUG', true );

    这样我们就能在页面看到想象的错误提示。

  2. 当发现是插件错误导致时,因为此时已经无法登录后台的插件管理页面,这时,直接删除wp-content目录下对应的插件目录就可以,这时wordpress会检测插件失效,从而跳过插件代码逻辑。

如果还有其他问题,可以进一步结合php nginx等日志进一步分析。

unraid安装windows10

最近在体验unraid。打算装一个windows,用来运行一些window专有或者体验好的软件。因为开始各种装不上,所以期间参考文章,体验了了灯大的精简img,发现实在用不下去,所以还是研究自己怎么装。

以下文章为自己的的安装过程,因为硬件或者版本可能会有差异,请读者自行消化。

windows 的下载

个人建议下载微软的原版,具体自行搜索。

硬件配置

nas-inf

windows10 vm config

windows10 vm config

安装

按照类似的配置正常应该能进入window的那个窗口的界面,显示进入了安装引导过程,如果没有反应或者其他错误,请尝试更改Bus类型,和vDisk。如果安装中断,那么会导致再次启动直接启动系统无法成功,请删除vDisk,重新创建。

无法找到安装磁盘

安装中常见的问题,在正常按照引导进入安装界面过程时,无法找到磁盘,解决方案是安装虚拟io驱动,分别选择以下驱动,并进行下一步:
Balloon
NetKVM
vioserial
viostor(一定要最后加载这个驱动)
然后就看到你分配的容量的虚拟磁盘了,然后正常分区和格式化就可以正常安装了。

再安装成功进入windows界面后,建议安装驱动盘中的qemu-ga-x64.msi

安装成功

window10

win10开启smb共享

默认没有开启时,通过run 访问smb访问时,会提示:
Fix can’t access this shared folder because your organization’s security policies

开启方法:

  1. windows+R 运行 gpedit.msc
  2. setting enable
    Computer configuration > Administrative Templates > Network > Lanman Workstation.

From your left hand side click on the Lanman workstation, Now you can see the Enable insecure guest logons policy on your right hand side.

open Gpedit and click on the policy settings

Double click on it to open the Policy settings. Change the Policy setting to Enable and click on apply and OK.

Fix can’t access this shared folder using Gpedit

  1. restart
    Now restart the system once and try to access the shared folder.

sata电源线线序

周末整理电子垃圾,发现自己需要一条sata的电源线,来进行外置sata的的测试。但是没有现成的线,只能自己改装一条了。

不同配件的sata电源线会有差异,5pin或者4pin,5pin的是包含3.3V的完整配置,4pin的没有3.3V。

一般是
黄 黑 黑 红
或者
黄 黑 红 黑

如下图:
sata电源线

sata标准的定义如下:
sata电源线线序

根据上面的定义可以得知:
黄色:12V
黑色:reserved ,实际用万用表测试是GND
红色:5V
黑色:GND

因为有的线没有3.3V,所以可以看到很多转换板上都是要增加1117之类的LDO转化芯片,就是为了实现5V转3.3V。

基于阿里云平台和CSDK的air724ug fota流程

前序

几个月前就完成了阿里云sdk的移植和集成,基本流程跑通,就继续干别的了,今天王正式产品中集成,发现还是费了些时间回想当时整个过程的一些细节,所以今天就详细记录下,争取下次再更一步节省时间。

ota升级文件制作

目前合宙air724ug由于flash空间的分布和设计,目前的ota只支持app的升级,无法支持整机core+app的升级。所以这也就是下面的步骤中,生成差分版本时,基线是core。

制作基线版本的fota bin

  1. 运行./xxx.bat fota=core hex目录下会生成基线版本(只有自定义服务器才需要基线版本(因为网页差分工具需要),使用合宙服务器不需要)。 xxx为自己项目的编译脚本名。
  2. 这个基线版本是跟随每个刷机版本对应的,每一个刷机版本都应该有fota bin文件,以备制作ota升级文件时需要。

制作新版本的bin

  1. 运行命令:
    运行./xxx.bat fota=app hex⽬录下会⽣成只升级APP的新版本
    运行./xxx.bat fota=core hex⽬录下会⽣成只升级CORE的新版本
    运行./xxx.bat fota=all hex⽬录下会⽣成APP+CORE的新版本
  2. 因为我们是要针对core来制作app部分的差异,所以这里我们需要选择all:
    ./xxx.bat fota=all

生成差分文件

这里需要借助合宙的在线工具:http://doc.openluat.com/chafen
然后将生成的文件下载下来。

需要说明的是,差分出来的文件时比直接用fota=app生成的bin要大的,也就是说明这个差分中还有一些版本结构配置相关的数据,并不是把app扣出来而已。

阿里云ota任务创建

路径:物联网平台/监控运维/OTA 升级
https://iot.console.aliyun.com/ota/list
这里不展开,如有需要,日后再单独开篇。

硬件fota

目前的代码是直接移植的alifota SDK,需要自己是移植和适配必要的接口,目前我手里的是已经移植和调试过的,今天主要记录相关的关机日志路径,便于日后定位问题和熟悉过程。

  1. 向平台提交当前产品版本
    [aiot_ota_api.c:1105] [INF] [alifota] top:/ota/device/inform/产品key/产品ID, payload:{"id":2, "params":{"version":"alifota1.0.0"}}
  2. 服务器回复:
    [aiot_mqtt_api.c:1208] [INF] pub: /ota/device/upgrade/key/id{"code":"1000","data":{"size":78911,"sign":"2373a5a6848e7e2021e2ca8befd3e0e5","version":"alifota1.0.1","url":"https://iotx-ota.oss-cn-shanghai.aliyuncs.com/ota/f6fa639b5e67a6
  3. 解析ota文件信息
    [ota.c:214] [INF] [alifoat]OTA target firmware version: alifota1.0.1, size: 78911 Bytes
  4. mq建链
    [rda8910_port.c:93] [INF] [alifota] create socket ...
    [rda8910_port.c:130] [INF] [alifota] tcp connect to addr iotx-ota.oss-cn-shanghai.aliyuncs.com:80
    [rda8910_port.c:141] [INF] [alifota] tcp connect success
  5. 开始传输
    [aiot_ota_api.c:950] [INF] [alifota]ln 950 fuc _http_recv_handler,download 0 78911
    [core_http.c:581] [INF] [alifota] ln 581 fuc core_http_recv,res:512
    [aiot_ota_api.c:501] [INF] [alifota] ln 501 fuc aiot_download_recv, download :512 512
  6. 传输下一个块,每次512字节
    [ota.c:402] [INF] [alifota]++++++++++++++++++++++++ start 512 end 1023
    [core_http.c:581] [INF] [alifota] ln 581 fuc core_http_recv,res:512
    [aiot_ota_api.c:501] [INF] [alifota] ln 501 fuc aiot_download_recv, download :512 512
  7. 重复以上的步骤
  8. 接受最后一个包
    [aiot_ota_api.c:950] [INF] [alifota]ln 950 fuc _http_recv_handler,download 78848 78911
    [aiot_ota_api.c:975] [INF] [alifota] download size ok
    [aiot_ota_api.c:982] [INF] [alifota]digest matched
    [aiot_ota_api.c:986] [INF] [alifota]ln 986 fuc _http_recv_handler,percent 100
  9. 向平台上报进度
    [aiot_ota_api.c:1105] [INF] [alifota] top:/ota/device/progress/key/id, payload:{"id":22, "params":{"step":"100","desc":""}}
    [aiot_mqtt_api.c:465] [INF] ln:465. [alifota] mqtt send success:96 0^
  10. fota 成功
    [ota.c:477] [INF] [alifota] ln 477,func:alifota_main, deinit mqtt
    [ota.c:481] [INF] [alifota] ln 481,func:alifota_main, deinit ota
    [aliyun.c:71] [INF] alifota success

coolwatcher日志

硬件日志

nginx下proxy根路径模式的letsencrypt续期问题

问题

当我们使用letsencrypt时,我们会使用renew来自动续期免费证书,从而实现永久的免费证书使用。

在某些域名的重定向场景,我们通过 location和proxy方式将某一个子域名重定向到新的子域名下。这种情况下我们的https证书一般是挂在原域名下的,但是因为我们是从根域名就重定向了,这就导致letsencrypt在renew过程中验证webroot会失败,因为域名的访问是走proxy路径的。

解决

我们给letsencrypt专门配置一个对应子域名能web访问的路径,并且将配置项放在前面,因为nginx的location是按照顺序匹配的,一旦达成就会退出location过程,所以就实现了对letsencrypt路径专门做映射管理的目的。

letsencrypt的check路径如下:

https://xxx.xxx.com/.well-known/acme-challenge/xxxx

所以我们的nginx location 路径配置如下

location /.well-known/ {
     root  /xxxxx;
}

其中的root路径就是子域名所对应的可访问web本地路径,这个路径是专门为了renew创建的。这样就保证了letsencrypt的renew同时保证了子域名的其他业务路径正常proxy。

修改配置后,切记重启nginx,否则就要该挠头了:)

VSC remote development

remote ssh config:

Host xxx
  HostName xxx
  Port xxx
  User xxx
  ForwardAgent yes
  IdentityFile xxx

IdentityFile 不是合成的ppk文件,是私钥,不要搞成公钥,后缀不带pub

settings.json相关配置

"remote.SSH.showLoginTerminal": true,
"remote.SSH.configFile": "C:\\Users\\xxx\\.ssh\\config",
"remote.SSH.useLocalServer": false,
"remote.autoForwardPorts": false,
"remote.SSH.remotePlatform": {
"xxx": "linux"
},
"remote.SSH.path": "D:\\Program Files\\Git\\usr\\bin\\ssh.exe"

注意:

  1. xxx要改成自己的实际server名称
  2. 要配置ssh的路径,否则ssh命令会失败提示“An SSH installation couldn't be found”
  3. 左下角绿色了,就标识成功,终端会显示“6b8492386046: end”类似字样
  4. 过程中提示git版本过低,提示的是服务器端的版本,根据需要升级,不是强制升级

air724ug驱动无法识别问题

之前装过驱动是好用的,有一阵没用了,大概半个月,今天开机,发现windows死活无法识别usb,三个大叹号。

重新执行了安装,也重新启动了,无果。

后面手段选择安装驱动,选择的路径:DriversForWin78\Drivers 然后成功,重新可以下载了。

记录下,就这样。

《格局》书摘

格局

吴军老师的《格局》不错,建议搭配《见识》一起阅读。

书摘

  • 为什么我坚持每个人都要分享利益,感激他人的贡献呢?因为一个大型组织只有这样才能形成合力。
  • 很多组织或机构中没有人愿意独自做决断的原因,不是他们不喜欢拥有决定权,而是害怕承担责任。
  • 当一个人愿意承担自己的责任时,决断就不难了,效率也就提高了。
  • 很多人离职并非因为第二家公司比前一家好,而仅仅是因为他们在前一家公司遇到小小的不爽,就把之前的好忘了个精光,愤然离职。
  • 我们要做的是超过他人的长处,而不是满足于超越别人的短处。
  • 无论多么强势的人都难逃命运的安排,认命其实是我们每一个人都应该有的生活态度。
  • 人贵在自知,知道自己的长处,知道自己的能力边界,在这个边界内最大化自己的收益,这是一种积极的人生态度。至于结果时好时坏,不妨泰然处之。尽人事,听天命。
  • 问心无愧是我们唯一稳得的报酬。
  • 但凡觉得自己了不起的人,通常都没见过真正聪明能干的人。
  • 比才能更重要的是见识,而在见识之上还有运气。
  • 上天不会亏待一个真正努力的人,但也不会同情假勤奋的人。
  • 如果因为你做的一些事情,哪怕很小,哪怕微不足道,但世界因此不同,那么这就是你给世界留下的遗产。
  • 休息的本质是从外界获得信息和能量。
  • 3P:purpose,pleasure,pride。
  • 一个帮助过你的人,比一个你帮助过的人,更愿意帮助你。
  • 一个人在选择工作单位时,应该把对自己好,能帮助自己成长的公司放在首位,而不是觉得某公司很酷、很热门或者多给了一些薪水就选择它。
  • 人立于天地间,必然有出路。
  • 人最重要的是生活着,快乐着。
  • 成就=功功率X影响力X速度。
  • 一个公司在规模不大时,在关注度上和大公司全面竞争是没有意义的,它更应该关心自己的核心用户,关心自己能给他们带来什么价值。
  • 超越免费的第一条是制造一种稀缺性,而这需要产品、服务本身具有一种难以复制的特性。
  • 眼睛只盯着看得见的钱的人,一辈子不会有什么大出息。
  • 第一级的工程师的贡献是第二级的10倍,第二级是第三级的10倍,以此类推。
  • 一堆三流的人聚在一起,有时带来的麻烦,比他们能解决的问题还多。
  • 10个90分都抵不上一个100分,因为卓越和良好之间的落差是巨大的。

xcode中如何抑制告警 Double-quoted include in framework header, expected angle-bracketed instead

问题

编译时会有大量类似“Double-quoted include in framework header, expected angle-bracketed instead”的告警,从内容我们理解是双引号和角括号的问题,如果我们不希望提示,该如何做?

solution

先从左侧project 栏选中项目,然后进入到 Build Settings ,然后找到 Quoted Include In Framework Header,然后选 No

蓝牙AoA AoD

蓝牙aoa aod

蓝牙aoa aod,对于不熟悉具体技术的人来说,并不能很好区分其中的差别。
下面将通过两张图片,来浅显的解释这两个概念的差别:
aoa

aod

从以上两张图,我们可以这样简单的来理解:
aoa:到达角定位,通过到达接收机的多路天线信号差来定位的方式。解算在基站侧,基站多天线接收。
aod:离开角的定位,通过离开发射机的多路天线信号差来定位的方式。解算在标签侧,基站多天线发射。

初入大连

伴随着渐渐模糊的记忆,很多真实的片段,我已经无法分辨是否是梦境中的残留。

大约是2005年的3月的某一天,我异常兴奋的和一群校友,去人生中的第一家公司报道,我们大约两三个班的同学都选择了同一家公司,这是巧合,也是有因,后面再表。

这种第一次到异地的感觉,是无法言表的新鲜和激动。那时的大连,一方面有bo在经济方面的成就,一方面是对日外包的窗口,经济蓬勃,又有旅游城市的效应,城市年轻而有活力,而软件园作为当时大连的支柱产业,园区显得格外崭新和设计时尚。

我们一行人坐火车,从哈尔滨出发,到达大连,然后坐大巴直达软件园,似乎到达的是早晨的时间,停靠在当时还是叫通用大厦的门前的那条路上,那种感觉和震撼,就像我第一次看到大学初恋的感觉,人生中仅有的几次,哈尔滨,看见她,落脚大连,初到深圳,那种感觉在第一个地方呆久了后,再也没有出现过,仿若梦幻。

我们落地后,给了似乎是7班还是6班的其他到东软的校友电话,请求暂放了行李,然后我们去软件园广场的公司去报道。

再后来听说接待我们的校友不久后就离开大连去北京了,我们再无联系和音信。

我们最近的一行4人,来自3个版,7班,8班 9班,在一个公司,开始了为期3个月的一起上课,一起学习,一起下班做饭,一起打牌的美好时光。

我们白天上课练习,主要是基于tengin嵌入式操作系统的学习和练习题。除了我们校招的预备役员工,还有社会交钱的培训学员,当然还有一些后面没通过测试的,没有成为正式的员工。除了我们学校,还有很多其他学校的应届本科,硕士,好像没有博士。我们们有微薄的补助,具体是多少我忘了,反正够我们租房和吃饭了,相对于在学校没有收入的我们,已经不错了。

实习为期三个月,我们除了要学习基本的东西,还要学习日语,通过相关的技术测试,同时也顺便基于我们的开发项目完成一个相关的嵌入式毕业设计。当然因为学员众多,不可能每个人单独辅道,所以最后大家的毕设都大同小异,但是好在是不同班,也没有什么影响。

毕业设计是围绕我们学习的tengin,自己选一个驱动课题,然后就基本的操作系统原理,接口,驱动设计思路,实现代码,等等到最后可以在开发板演示的程度,这些素材来完成一个驱动开发的相关毕设,虽然大家都是相同的操作系统方向,但实现细节和内容并不首先和相同,还是有很大发挥空间。我记得自己的大概内容是一个模拟shell的cf card的交互式命令行系统,可以通过诸多自定义的shell 指令来 操作cf card ,自己实现了一套指令。这个课题本身三个月来说难度并不小,但是因为很多人类似的内容,又显得并无十分突出。

在说说我们入职的几个人,除了我们一起租房的4个,大概还有几个人,除了计算机系的还有自动化系的,我们共同的联系是我们可能都在实验室给相同的老师平时做项目,或者给几个认识的老师做项目,而我们校招时尽然选择了同一个主做嵌入式方向的公司,也算是大家对自己技术方向的认同吧。

那时我刚毕业税前2800。在我2007年4月份离开时,我是我们那一届的优秀员工,第一年工资就长了不少,2007年离职时,大概是接近4000,后面我被人挖走当时还替我缴纳了一年多的提前离职违约金,大概一万多,后面有空再表。

那个时候很轻松,有很好的环境,那时的大连it公司基本对于一个毕业生来说是相当现代和梦幻的。中午有很好的自选快餐盒和水果。晚上我们很早下班,回住处4个人分工做饭,有焖饭的,炒菜的,日子过得真惬意。六月底回学校答辩时,体重三个月长了20斤,就是最好的证明吧。

在大连的一帮同学和同事 也是我人生中最亲密的朋友,很多至今还保持着联系。

我们当时自己租房,我记得是800的一个军工区的红房子,四个人四张铺地的垫子,买了一台电视,这个就是我们仅有的家具家电,对了,还有电饭煲。

我在学校是对51汇编很擅 长,课程设计和平时的课程都做的很好,所以也有契机跟专业课老师做了些参加学校科技大赛的嵌入式项目,那时在学校学习的protel画板子,调试汇编单片机,做竞赛,没想到后面竟然影响了我后面15年的职业生涯。人生每一个改变都可能是契机。

基于在学校的技术方向和大四的整个项目经验,我自然而然的选择了嵌入式方向,在校招的大厅,我选了一家大连的初创公司,我们大概是第二批员工,第一批员工后面也成为了我们的朋友。这家公司后面被股权吸收成为了软件园下属的全资子公司,那是后话,那时我已经离开了,去了另外一家做工控的小公司。

崭新的城市,新的公司,还有崭新的漂亮姑娘们,一切好事兴奋,新奇。我们中间的胡某似乎对一个漂亮女生表示过,注意我说的表示不是表白,因为好像没有那么轰动和强烈,毕竟我是后面从其他人口中听说的。那个女生听说是有本校的男友,所以表表示并未成功,今天看,我很羡慕和敬佩他。

说道一个好玩的细节,我们一行大约30人,有计算机系的,有自动化系的,有数学系的,也有几个是吉大的日语系的,因为当时的嵌入式业务还是偏日本的外包方向,打印机设备,自动化机械控制设备,嵌入式设备等,所以很多原始需求书都是日文的。一起招聘的日语系女生,也是我们培训期间的日语老师,给我们平时培训日语,至今我能熟练日语的50音图,也是那时学习的,还会几个基本常用日语短语,其他的我基本都忘了。

我们转正后,先是在后面所说的通用大楼上班,后面搬家到了转盘的软件园大厦,等我07年离职时,已经搬到七贤岭了,公司的人员规模在迅速扩大。

那时的通用大厦和软件园等设施都是一流的,因为很多公司和项目都是和日本员工一共办公的,所以对于刚毕业的我们来说这种条件简直是,现代化的没法表达,加班也不猛,至少比现在的深圳的的工作来说,我都不记得在大连加过什么班。

那时我们的工作内容大概是这样的,我们会接到日本外包的项目,有原始的日语文档,翻译们会帮我们翻译,然后我们开始研究文档规格,根据对应的实际设备或者指定开发板,我们开始完成相应的功能,然后由测试按照规格书进行功能验收。这期间包括嵌入式的开发,和pcba的设计,那时还主要是arm7和arm9,比如xscale270,三星的2440等主控。我们当时主攻的是日本oki的mcu系列,配套日本的嵌入式操作系统tengin。这个tengin可比淘宝后面那个tengin早多了,但是大部分人应该不知道这个实时嵌入式操作系统。

等到7月份我们正式毕业入职时,我们在大工对面的写字楼上班,我们在大工教师职工区的红房子,还是四个人一起合租了房子,每天大概上班去公司,走过去5分钟。

我在学校主要是汇编,C语言比较厉害,mfc做上位机,画板子是protel。毕业从2005 7月到2007 4月eda方面我基本已经转到cadence和allegro了,因为前辈也是我后面的同事,都是在用cadence和allegro。这两个工具也成为我2012年创业时的主要利器,人生处处是惊喜,后面有机会再表。

除了画板,调试,焊接,开发上位机,调试嵌入式驱动,可以说毕业的几年是我人生中学习最快乐成长最快的时候,我后面的整个职业方向和开发方向都跟这期间的学习和积累有关。

前面说到我住在大工,是的,有个很方便的地方就是我们可以去学生食堂吃饭,我们也可以去北门附近的网球场去打球,经常也看到留学生在北门教学楼前玩飞盘,那时好羡慕。关于网球,我人生中仅有的一段联系时光就是那时,后面再无机会解除。

一个有意思的话题是,我们那时没有女朋友,仅一个有的还不在大连,等于基本单身状态,我们唯一的娱乐项目竟然是做饭,特别是周末的大餐。我们经常会做的很多,觉得吃不完的时候,我们就会很默契的说,让你那个大工同班同学来吃饭吧,好在我们的手艺都还练的不错,他每次来都吃的很开心,毕业很多年后,我们还会在同学微信群里谈起我那时的饭菜,时光真好。

当时的大连,除了软件园(既指园区房地产企业又是it企业)还有华信和东软,类似今天我们提到的深圳的华为和腾讯一样。那时我们这三个企业都有校友,似乎哈尔滨毕业的不是去北京就是来大连。这其中软件圆似乎是名气和实力最差的额,今天过去了15年,我已经不知道具体的情况了,反正当时是那样的感觉。除了我们这一波选择嵌入式,其他的都是纯软件的,基本都是java或者cobo(记不清具体发音了)的方向。

说到大连,当然不能不说那时的星海广场,号称当时亚洲最大的广场,还有当时全国除了北京外唯一的大型华表,后面被bo事件给拆了。星海很漂亮,海边也干净很适合游玩和散步,那时离软件园也不远,想想现在在深圳,好生羡慕,但是,人生无法回头。

那时我印象中服务最好的是软件园的浦发银行,然后是黑石礁的招商银行。多年后我在深圳还特地申请了一张浦发的信用卡,给了我我1500的额度,虽然我不懈一用,仍然保留了好多年,前两天刚注销,就是因为那时毕业时,浦发给我留下的美好印象。现在我是招商的主力用户。

实习一共三个月,每天学习交作业,有老师(前辈和后来的同事)给审批,结束时进行考试,然后通过的就可以正式入职,没通过的就当花钱或者免费培训了。有几个没过的,具体我已经忘记名字了,时间太久远了。

对于第一份工作,我有着很多的记忆,就像你的第一个初恋一样,你会记忆一辈子。漂亮的办公环境,良好的学习培训机会,很技术的项目,漂亮的女同事,这份工作几乎满足了我对于一份工作的所有美好要求。因为工作的原因,我们会接触很多日文的材料,我对日语的兴趣和日本文化的兴趣也是那时培养的。

那时虽然技术的很多工作我都有参与,但是更多的还是辅助同事,焊接,调试,测试,去采买元器件基本我都干过。有时候还出差去保税区,去日本客户工厂调试设备,那时很忙碌但是好像没有现在的这种沉重和压力感。

在学校的时候很穷,学的计算机,却自己没有电脑,所以大四基本是在实验室猫着,用实验室的电脑练习。 毕业后第一台电脑是大工学生的二手电脑,amd的处理器,很便宜,基本也能用,也能打红警,不错了。后面正式工资发放了,有了积蓄,就当时花了5000左右买了叫惠普康柏的笔记本,那个本现在还记忆尤新,毕竟是我的第一台笔记本,后面因为散热死机而寿终正寝。

大工的校园不错,我记忆中有很多类似丁香的常年白色小花树,芳香而不艳丽。 学校很大,人很多,我们行于其中,每日匆匆,却未想此生竟再也不遇。

我经常买元器件的电子市场的大嫂,人很好,有一次很晚了,留我去她家吃了饭,具体饭菜饭菜我忘了,我竟此后都无法相遇和表达感谢,希望她和家人都身体健康吧。

那时公司人很多,好像没记得什么通宵加班的事情,业务也是严格的工程划分,开发,设计,还有一帮测试的小姑娘,现在想想真实很清空的氛围。

当时在学校的竞赛课题是单片机控制rf431做短距离无线双向通讯,后面工作了,是调试xscal270开发板,基于okI的arm7做了 自售机和palm之前的红外适配器,用户parm管理自售机数据,客户是富士冰山,不知现在是否还存否。

那时开始接触嵌入式linux的概念,虽然当时公司主攻tengin,但嵌入式linux palm等已经进入视线,那时windows mobile还没兴起。安卓还没出头。

工作后主要用c做嵌入式开发,用mfc 做桌面开发,后面用evc做windows mobile开发。业余还做些学习项目,第一个挣钱的是给本溪的一家当铺做一个基于mfc的当铺管理软件,使用access数据库,支持局域网多机,那个项目写了很多代码,可惜当时没用类似github的东西,并没有把代码保留下来,多次更换电脑后,就彻底丢失了。

这个第一份工作大概维持了一年8个月,工具上从protel升级到了cadence系列,平台从单片机升级到了arm9,开发也从汇编升级到了c、mfc,工资从2800升级到了3700左右。技术上基本在软硬件一体化的层面上更上了一个层面,慢慢接近软硬件独立一体化,这个也促成了我第二份工作,对方还给我付了违约金。让我提前出来参与项目。

那时刚从哈尔滨到大连,刚从学生开始上班,穷,舍不得花钱,也没女朋友,大把的时间,可是没钱。记忆最多的业余活动好像是四个人打牌,打升级,不亦乐乎,傻乐呵。

现在回想,除了大学,第一份工作时期基本是我最轻松快乐的时光了。后面的时间越来越忙,因为我的工作方向和生活中不断有新的东西进来。

站在今天的起点,评价当时的时间,那是个全面学习的未成形阶段,各方都有进展,但是还未能独立单挑项目,但是我的斗志已经蠢蠢欲动了,我要追求更快速的进步。契机就是第二份工作,这个是后话。那时爱情还没有进展,穷,长相一般,内心自卑,所以也未在爱情方面有刻意目标,主要还是工作方面的进展。

那时的公司氛围轻松,公司会经常组织聚餐,踢球等活动,聚会时大家也都很年轻很嗨,现在想想,真是一个黄金一般的年龄。

时间过去了15年,但是当时的第一份工作,竟然和今如此的相似,设计pcba,开发嵌入式,选型设计,也许一切都是命中安排好的,只是我们都不自知。

招聘我的是我们当时的老板,王老板,我不想写他全名,算是尊重吧,我离职后也再无交集,偶尔听老同事谈起,希望他也安好。

还有丁哥,最想写的就是你了,但是还是放到心里吧,人生何必再相逢,一切尽在不言中。

累了,先写这么多,有心情了再补充。

再见,大连
再见,我的第一份工作
再见,我落地的那第一束阳光
再见,那些模糊的甜美的微笑
再见,那个激情澎湃的年纪
再见,那年轻的朋友们
我想你们,我想念那时的自己

2020.09.06

[RDA8910 CSDK系列]724ug 差分fota之证实-附带ota简要流程分析

这两天在学习csdk系统,昨晚销售小哥给了一个链接:http://doc.openluat.com/article/1416/0 其中零妖大佬说了关于cat1 差分升级的事。

本着求实的精神,就很像知道确凿的证据,最好有代码或者数据的依据。

所以今天特意准备了下评估板子,看看能不能拿到些直观的线索。

第一个想到的就是通过coolwatcher追查系统的日志。这里有个开发板和我的usb uart小板电平不匹配的小插曲,定位此问题又额外浪费了点时间,暂不展开,如果有人想细究再补充,先说结论:想稳定看coolwatcher的日志,你需要使用host log的串口方式,而不是用usb方式。

测试使用的是官方的ota平台: iot.openluat.com ,设备初始代码是自己编译的测试包, ota包使用同样的代码,只是改了下版本号,因为主要是验证流程,所以不用关注功能逻辑。

fota包的下载使用的是http,注意是http,不是https,主要原因就是简单,https是趋势。

第一步:

通过http获取到文件大小,这里对应是的是Content-Length

[ota] HTTPClientFindFirstHeader 21,Content-Length: 48330

[ota]GetSize fsz: 48330

6M左右的原始bin档,实际的下载大小只有48K左右,这就说明下载的是差分,而不是全量包。

第二步:

ota_parse otaProcess file size 48320, type 1

从48330 变成了48320 说明有10byte可能是控制描述数据,不细表。

第三步:

[ota] HTTPClientReadData readSize 511

[ota] HTTPClientReadData readTotalLen 1022, 48330

每次下载511个字节,一包一包开始下载

第四步:

中间重复无数包,通过总包大小控制下载次数,直到最后一包下载完成

[ota]--HTTPIntrnConnectionClose--

ota_parse otaDownloadDone appState 3 CoreState 1

[ota_parse] FILE: app 48320 0

app应该是文件名,48320是大小,0 是属性或者权限之类的吧。

第五步:校验

ota_app crc (f7ddf30f,f7ddf30f)

ota openat_newApp appImageIsVaild ok

使用的应该是crc32 的校验的多包累加和,纯瞎猜,不求证

第六步:更新ota的标志

ota check 1,0

ota check 1,1

ota OPENAT_newOtaFlagSet 102

这个102应该就是告诉bootloader这个是刚ota完成,第一次需要做些特殊的工作。

第七步:下载完成

ota_parse _otaDownloadDone ok

[ota]fota end 0

看日志,整个升级48k大小的样子,花了6s完成,基本无感知,所以如果你用usb方式看日志,可能coolwatch还没连接上,升级就完成了

nordic 如何使用单分区OTA模式

ref:https://devzone.nordicsemi.com/f/nordic-q-a/48364/v15-3-dfu-use-single-bank-directly

v15.3 DFU use single bank directly

Note that for SDK v12.3 : if application size * 2 > available space a single bank update is performed.

While I am using v15.3 and there seems no more infor about the single bank and dual bank settings, I want to free more space for data storage and disable dual bank dfu, my question is how to use single bank dfu directly?
solution1: final I find the NRF_DFU_SINGLE_BANK_APP_UPDATES in https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v15.3.0%2Flib_bootloader_dfu_banks.html&cp=5_1_3_5_1_2

solution2: increase the NRF_DFU_APP_DATA_AREA_SIZE to make the space less than dual bank.
Thanks for answering your question. Just want to add that option two may be better as it will prevent the bootloader from overwriting the app data in case an image is also too big for a single bank update.

refs:
Dual-bank and single-bank updates.pdf

IOS device support

问题

当你升级了macos,xcode自带的iphone支持版本和你iphone不一致时(比如手动升级的beta版本等),这时候xcode是无法正常调试和下载ios app的。

解决办法

增加对应ios的DeviceSupport文件,手动添加到xcode的对应目录:

更多请见:
https://developer.apple.com/download/
https://developer.apple.com/download/more/

起点

一直在等待的那个开点,那是一个既许了无数幻想和冲动的起点,那个演绎了无数次,却始终无法清晰和开始的起点,此刻,伴随着熟悉而奇怪的音乐涌起的冲动,一个声音在告诉我,是的,就是现在,我感受到了起点,竟是是如此普通的时刻。

pads settings

  1. exec path

    \MentorGraphics\PADSVX.2.3\SDD_HOME\common\win32\bin\powerlogic.exe
    \MentorGraphics\PADSVX.2.3\SDD_HOME\common\win32\bin\powerpcb.exe
  2. how to set the "PADS Projects" path

    \MentorGraphics\PADSVX.2.3\SDD_HOME\Programs\powerlogic.ini
    \MentorGraphics\PADSVX.2.3\SDD_HOME\Programs\powerpcb.ini

clang-format 参考配置

贴一份在用的参考配置,欢迎大家讨论

BasedOnStyle: LLVM
Language: Cpp
ColumnLimit: 200
# 允许对注释排版
ReflowComments: false
# 允许修改头文件顺序
SortIncludes: false

#括号风格
BreakBeforeBraces: Custom
BraceWrapping:
  AfterFunction: true
  AfterEnum: false
  AfterStruct: false
  SplitEmptyFunction: false # 空函数,括号可以同行

# 注释左对齐
AlignTrailingComments: true
# 单行函数也要换行
AllowShortFunctionsOnASingleLine: false
## 是否允许短if语句单行
AllowShortIfStatementsOnASingleLine: true
# 单行循环可以单行,比如while1 和空for等
AllowShortLoopsOnASingleLine: true
# 自动换行后,二元运算符总在前
BreakBeforeBinaryOperators: true
# 缩进宽度
IndentWidth: 4
# 连续声明时,对齐所有声明的变量名
AlignConsecutiveDeclarations:   true
# 针和引用的对齐: Left, Right, Middle
PointerAlignment:   Left
# 在C风格类型转换后添加空格
SpaceAfterCStyleCast:   false
# 在赋值运算符之前添加空格
SpaceBeforeAssignmentOperators: true
# 开圆括号之前添加一个空格: Never, ControlStatements, Always
SpaceBeforeParens:  ControlStatements
# 在空的圆括号中添加空格
SpaceInEmptyParentheses:    false
# 在尾随的评论前添加的空格数(只适用于//)
SpacesBeforeTrailingComments:   2
# 在尖括号的<后和>前添加空格
SpacesInAngles: true
# 宏赋值对齐
AlignConsecutiveMacros: true