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。

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.3
  2. cache目录要有写入权限,否则无法正常展示页面,需要通过php-fpm错误日志来定位。

使用

  1. 配置对应的php支持,一般就是设置nginx或者php-fpm相关fastcgi配置项
  2. 需要安装在域名的根目录,在子目录会有问题。
  3. 放在根目录后,可能会暴露不想暴露的文件和目录,在根目录创建 .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。

VSC remote development

ssh config:

Host xxx
  HostName xxx
  User xxx
  ForwardAgent yes
  IdentityFile xxx

IdentityFile 不是合成的ppk文件

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版本过低,提示的是服务器端的版本,根据需要升级,不是强制升级

《格局》书摘

格局

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

书摘

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