分类 未分类 下的文章

  1. 美洲: 除了北美的美加两国说英语, 巴西说葡萄牙语外, 其他各个国家几乎都说西班牙语.
  2. 东南亚: 几乎各个国家都说各自的语言. 少数国家如新加坡, 菲律宾还使用英语. 当然新加坡通行4种语言,还包括华语,马来语和3. 泰米尔语. 另外老挝,缅甸,泰国本族语言是汉藏语系的衍生, 跟我国壮族,侗族,傣族等语言比较接近.
  3. 中亚: 中亚五国虽然都有自已的语言, 但由于苏联时代的影响, 俄语比较通用.
  4. 中东: 阿拉伯语比较通用. 少数不同是以色列说希伯来语, 伊朗说波斯语, 阿富汗说普什图语和达里语.
  5. 澳洲: 主要通行英语.
  6. 非洲: 几大殖民势力的语言都有通用.
  • 英语: 尼日利亚、加纳、利比里亚、塞拉利昂、冈比亚、肯尼亚、坦桑尼亚、马拉维、纳米比亚、博茨瓦纳、南非、斯威士兰、津巴布韦、赞比亚等国家将英语作为官方语言,
  • 法语: 包括刚果(金)、刚果(布)、科特迪瓦、卢旺达、中非共和国、多哥、几内亚、马里、布基纳法索、喀麦隆、乍得、科摩罗、吉布提、塞内加尔、马达加斯加、毛里塔尼亚、阿尔及利亚、摩洛哥、塞舌尔等
  • 葡萄牙语: 葡萄牙语是安哥拉、莫桑比克、圣多美和普林西比等国家的官方语言
  • 阿拉伯语: 在北非和部分东非国家广泛使用,是埃及、苏丹、利比亚、突尼斯、阿尔及利亚、摩洛哥、索马里、毛里塔尼亚、吉布提、科摩罗和乍得的官方语言

根据metaso AI的总结, 主要小语种在中国开设专业的大学数量:
俄语:174至181所大学
西班牙语:超过100所大学(具体数字在不同证据中有所不同)
阿拉伯语:41至10所大学(数据不一致)
法语:148至71所大学(数据不一致)
葡萄牙语:39至6所大学(数据不一致)

如果要与海外做生意, EF有一个英语熟练度指标,每年都会发布,而且还分别做中文和英文版本. https://www.ef.com.cn/epi/
2023年英文版: https://www.ef.com/assetscdn/WIBIwq6RdJvcD9bc8RMd/cefcom-epi-site/reports/2023/ef-epi-2023-english.pdf
2024年中文版: https://www.ef.com/assetscdn/WIBIwq6RdJvcD9bc8RMd/cefcom-epi-site/reports/2024/ef-epi-2024-simplified-chinese.pdf
可以看出, 除了英语母语国家, 拉美虽然说西班牙语, 但英语水平也是不赖的. 欧洲也是如此. 甚至在西班牙和葡萄牙都有不错的成绩. 但是在中东, 中亚, 南亚, 以及非洲的非英语国家, 英语熟练度就比较差了. 很奇怪的是英语国家印度的德里竟然评价为极不熟练.
EF的这个指标主要是通过在线自愿测试得出, 且年龄中位数为25岁.

想做像素动画,例如64x20px, 该怎么做?
尝试了几种思路

用视频编辑软件,可以自定义分辨率为64x20

PPT + Gif编辑

  • ScreenToGif (简称S2G)可以录制gif, 导出gif也能导出视频.
    这样用Powerpoint做动画,S2G录制. PPT上尺寸大一些, 录制的动图可以在缩小为需要的大小.

游戏的PNG Spritesheet技术

游戏一般把精灵角色的多个动作合并到一张PNG中, 这样加载起来更快. 这个想法最适合我.

打算用微信小程序将数据存为UTF-8格式的CSV,让Excel编辑.
使用wx.writeFile或者writeFileSync可以设置编码为UTF-8保存. 但此后的坑就连续不断出现了.
通过shareFileMessage转给自己, 用Excel打开, 就会发现是乱码. 原因是Excel只支持带BOM的UTF-8. 开始解决这个问题, 然而微信和Excel都有坑, 分别说下:

微信的系统差异

要给文件前面加UTF-8 BOM, 最简单是在保存的字符串前加入 \ufeff 再用writeFile+UTF8格式保存. 你会发现Android版本微信很好地完成了这个任务, Excel顺利打开, 然而ios版本微信会把你加入的\ufeff自动删除! 保存了个寂寞

微信用binary方式保存

问遍教程,都会让你用Buffer和TextEncoder通过UTF8编码转为ArrayBuffer前面再加入BOM头0xEF、0xBB、0xBF, 但是微信小程序不支持这两个常见的类Buffer和TextEncoder. 除非你自己实现一个.

微信曲线救国方式

碰到ios, 先保存,再读取,再在前面加入BOM头,再保存. 利用微信writeFile保存功能支持UTF8来解决

    tableStr = '\ufeff' + tableStr // 增加UTF8 BOM头
    const fs = wx.getFileSystemManager()
    fs.writeFileSync(filename, tableStr, 'utf-8')
    const deviceInfo = wx.getDeviceInfo()
    if(deviceInfo.platform == 'ios'){
      // 由于iOS系统上会把utf8保存的文件的前面的bom头删除(android不会),所以需要以arrayBuffer的方式读入文件之后重新加上utf8 BOM头。
      // 之所以不在内存中完成转换,而是通过文件读写的方式转换, 原因是内存中转换字符串到Utf8的两种手段Buffer/TextEncoder在微信小程序中都不支持。
      let file = fs.readFileSync(filename) 
      const bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
      // 将文件内容转换为 Uint8Array 并合并
      const contentArray = new Uint8Array(file);
      const mergedArray = new Uint8Array(bom.length + contentArray.length);
      mergedArray.set(bom, 0);
      mergedArray.set(contentArray, bom.length);
      fs.writeFileSync(filename, mergedArray.buffer, 'binary')
    }

Excel的坑: 从2016版本才开始支持CSV UTF-8格式

如果你是2016以前的版本就不要挣扎了, 还不如用Win10/11上的记事本编辑了.

人也浪漫,字也浪漫,玉树银花自凋零。
来也如风,去也如风,犹记桃花笑盈盈。
弱水三千饮一瓢,我自逍遥。
莫为我死悲寂寥,翩然舞蹈。
-- 2024/12/4 纪念琼瑶去世

我感觉是不能着急,陷入要么全部让AI完成,要么完全不信任AI代码的极端。
程序员做的工作应该转向需求分析、概要设计、详细设计这些工作,将框架内的编码和单元测试留给AI来做。

C

一开始的简单任务, 都可以作对, 但是一旦开始出错, 如编译错误, 往往比较难改对.
最好一开始就用git, 修改正确后就立刻commit, 否则成果容易毁于一句话.
composer开新的会话要小心, 似乎新会话像一个新程序员, 跟旧的会话风格大不一样, 还可能大幅修改已经完善正确的代码
cursor写一个函数或一个文件中的几个函数是相对安全的, 整个项目大幅修改文件, 往往陷入编译错误数个回合不能自拔

在以下情况出过错, 加入.cursorrules看能否防止他再次出错

  • 在 Makefile 中,条件语句不能放在规则内部。
  • 请尤其注意函数先后的声明顺序,以及函数调用的顺序。
  • for循环的条件判断如果是变量x>0,并且x在每个循环中递减时, x的类型不能为unsigned, 否则不能退出循环.

B站上看到这么个视频https://www.bilibili.com/video/BV1H14y1p72L/
了解到还有这么一种玩法.大概意思是在电脑上通过docker开android虚拟机, 然后手机远程连接, 就像换了个手机一样.
基于视频的说法, 步骤是:

电脑端 手机端
容器 docker
虚拟机 redroid
私网 Zerotier
远程屏幕控制 ScrCpy

如果都在内网似乎Zerotier就不需要了.
另外虚拟机还可以开在云电脑或服务器上, 应该也不需要Zerotier.
感觉还有简单的方式, 就是在虚拟机中安装向日葵来远程控制. 只不过向日葵安装在虚拟机, 有可能会受限于虚拟机权限, 而ScrCpy安装在宿主机, 理论上不受虚拟机权限限制.

市监局 https://amr.sz.gov.cn/
房屋编码查询 https://tybm.szzlb.gov.cn:10000/AddressWeb/#/
经营范围查询 https://www.jyfwyun.com/home

如果注册选择秒批, 在选择经营范围的时候, 只有150个可选, 而实际上经营范围有1000~2000个.而且这一步比较靠后, 可能填了很多内容才出现经营范围选不了. 这时候只能删掉重选了.

秒批和正常注册流程有什么不同?

正常注册相对于秒批:

  1. 除了选择公司, 还能选择医疗或者其他要前置审批或特殊审批的企业.
  2. 公司名称里的行业用语可选项更多.
  3. 企业组织形式不限于内资有限公司.
  4. 股东不限于自然人
  5. 经营范围完整. 秒批只有完整范围的5%.

我的iqoo neo5又给摔了。 OLed屏幕不出意料的开始传染。因为第1次屏幕是我自己换的,结果到了vivo的门店里不保修了。工作人员说必须要原装屏幕才能享受折扣价450块钱换屏幕,否则这里850块钱还不如买新机了。
我看了一下二手机还没有换屏幕贵才六百多。
没办法,只好自己再买一个第三方的。上次买第3方的屏幕花了300多,当时就想不如换官方的屏幕。但是看到官方屏幕的价格是在我换屏幕之后。上次的第3方的屏幕有几个问题,一个就是显示区域小,第2个就是不支持指纹。
这次在淘宝上重新买了新的屏幕,价格260,据说支持指纹。这个屏幕带框架拆下来挺麻烦的,把所有的元器件全部拆光了,换到新的屏幕框架上。装完发现这个屏幕比第1次买的第3方屏还要小。第1次买的屏幕至少前摄像头是一个圆孔,这次直接变成了水滴屏。
测试完倒是确实支持指纹的。多点心理安慰。
在等屏幕的期间,我找备用机发现几乎所有的我用过的手机都被我屏幕摔碎了。看来屏幕在我手里摔碎是必然的。只是以前的屏幕都是lcd屏,摔碎了之后不会导致传染。
我找了一下现在比较强的lcd屏,发现iqoo z8是LCD最强的手机了,支持联发科8200,性能超出骁龙888。最大12gb+512gb。价格在1000~1200之间。
我看iqoo Z系列现在还在出新的lcd屏的手机,最新的z9用的是骁龙6系列的cpu,性能还不如z8。

主要政策

  1. 财政部 税务总局 发展改革委 工业和信息化部关于印发新疆困难地区重点鼓励发展产业企业所得税优惠目录的通知(财税【2021】42号) https://xinjiang.chinatax.gov.cn/zwgk/tzgg/202112/t20211221_93745.htm
  2. 财政部 税务总局关于新疆困难地区及喀什、霍尔果斯两个特殊经济开发区新办企业所得税优惠政策的通知
    财税〔2021〕27号 https://xinjiang.chinatax.gov.cn/sszc/zxwj/202107/t20210705_85628.htm
  3. 新疆困难地区重点鼓励发展产业企业所得税优惠目录 https://szs.mof.gov.cn/zhengcefabu/201910/t20191031_3413822.htm
    困难地区是指南疆三地州。南疆三地州是指喀什、和田、克州: http://cn.chinagate.cn/povertyrelief/2016-04/26/content_38327129.htm。霍尔果斯在伊利地区。
    总的来说,喀什可享受双重政策优惠。

    主要产业

    喀什本地农业特产是伽师新梅(西梅),近几年连年扩产。
    制造业在2023年增长大约30%。是增长非常快的产业。

进出口贸易

中亚五国:哈吉塔乌土,五个斯坦国。即:哈萨克斯坦、吉尔吉斯斯坦、塔吉克斯坦、乌兹别克斯坦、土库曼斯坦。其中哈吉塔从北到南依次与新疆接壤。其中哈萨克斯坦的面积最大,超过了其他4个国家的之和。哈国面积约为新疆+西藏。吉塔各自和广东差不多,乌土各自相当于四川面积。人口方面,哈国相当于深圳,约两千万人口,吉为600万,塔为1000万。乌3300万,土1000万。

喀什在全疆进出口贸易额排名第二。伊犁地区第一。主要面向中亚五国、共建一带一路国家。面向RCEP国家增长较快,但基数不明。
根据http://xian.customs.gov.cn/urumqi_customs/556645/556648/5679082/index.html 数据,2023年伊犁为931.1亿元,同比增长59.4%。其中机电产品增长65%,劳动密集型增长45%,说明其中机电产品正占据主流。其中90%以上为出口。99%为民营企业,57%出口到哈萨克斯坦。
喀什为836.8亿元,比伊犁少100亿,但同比增长71.2%,高于伊犁。其中主要产值来与喀什综合保税区,为496.5亿元,占比约60%。主要进出口方式为陆运,占喀什外贸进出口总值的88.5%。另外,喀什自贸区刚刚在2023年双十一落地(全疆自贸区包括乌鲁木齐、霍尔果斯、喀什)。其中机电产品增长103.8%,劳密产品增长55.7%。
乌鲁木齐以700.1亿元,同比增长36.5%,在全疆位列第三。从贸易伙伴看,哈萨克斯坦、吉尔吉斯斯坦、俄罗斯、乌兹别克斯坦、印度尼西亚为乌鲁木齐排名前五的贸易伙伴。中国新疆乌鲁木齐对上述国家进出口值分别同比增长48.5%、17.2%、59.7%、52%、8.3%。很神奇印尼这么远居然还有印尼。
以上可以看出:喀什是进出口贸易增长最快地区。乌鲁木齐应该是传统的进出口贸易地区,但增长已经相对比较落后。
喀什地区前四大贸易伙伴依次为吉尔吉斯斯坦、塔吉克斯坦、哈萨克斯坦、巴基斯坦。前两个是接壤国家。
http://paper.ce.cn/pad/content/202409/01/content_300396.html
喀什最重要的有个喀交会,其中还有专门与广东对接的分会场叫疆品南下、粤品北上。粤品北上主要是通过喀什进入中亚。

从喀什的国际贸易发展情况来看,跨境物流和跨境电商都处于快速增长时期。

其实,HTTP并不能直接访问云数据库,而是要通过云函数中介。也就是说,HTTP访问云函数,云函数访问云数据库。
在微信开发者工具中有个“云开发”按钮,里面可以管理云数据库和云函数。在如果小程序打开了云开发的支持,目录数中就会增加云函数的id目录,可以在这儿直接创建云函数。但事实上,创建云函数不止这一种方法,管理云数据库也不止这种方法,而要让http访问云函数,这里并不能开启http的访问权限。
事实上,云开发是属于腾讯云的一个服务,所以可以登录腾讯云的后台https://console.cloud.tencent.com/,找到云开发CloudBase这个服务。在环境管理中就可以看到微信小程序的对应的云开发环境。点击进去以后,选择环境配置-> Http访问服务就可以进行管理。
另外点击基础服务里的云数据库,页面将引导用户进入到一个专门的云开发平台的管理后台界面https://tcb.cloud.tencent.com/, 这里的功能微信开发者工具里面的云开发有点类似,但是设置的选项又要多一些。
腾讯云开发有专门的文档,跟微信者开发文档的域名不一样https://docs.cloudbase.net/,在这里也可以选择http访问服务的文档进行浏览学习。
这边的文档就提示进行云函数的创建和编辑,是有专门的命令行cli工具的https://docs.cloudbase.net/cli-v1/intro。这样可以通过命令行进行云函数的创建, 和微信开发者工具中创建的效果是一样的。并且命令行工具的功能要更多样一些, 比如说为http访问服务创建路由, 就只有用命令行工具才能做到。

示例项目配置

  1. 需要选择target为esp32c2
  2. 需要menuconfig配置flash容量和XTAL晶振。如果串口只能通过74880连接,说明晶振是26Mhz的,如果可以通过115200连接,则晶振是40MHz的。Flash如果配置不对,模组上电后会打印警告。晶振配置不对,蓝牙会找不到。我在淘宝买的这个https://item.taobao.com/item.htm?_u=ed01qc68e7&id=733669672310晶振就是26MHz、4M Flash,详情页说了4M Flash但是没有说26Mhz晶振。而Menuconfig默认配置是40Mhz+2MFlash。
  3. 下载乐鑫提供的配网APP。这个APP默认不会请求权限,需要自己去设置里面打开定位、蓝牙、附近设备等各项权限。
  4. APP打开后就能看到BLUFI DEVICE设备。

小程序实现

参考如下项目:
https://gitee.com/weijian.kang/esp-blufi-for-wx
该项目与上面的示例项目可以配网。不过仍存在一些问题,实际使用时候需要解决:
1、设备列表没有过滤,所有的蓝牙设备都会被扫描出来而不仅仅是blufi设备
2、错误的ssid和密码会导致界面卡在wx.showLoading界面。当然也可能是固件没有返回错误。
3、异步代码采用回调方式,回调地狱现象比较严重。最多的一行代码前面有42个空格(21个tab)
4、中文ssid发送的不对,在串口putty上显示的是乱码,而乐鑫官方发送的ssid在putty上显示正常
5、ssid需要手动输入
这个项目参考的另一个项目是
https://github.com/xuhongv/BlufiEsp32WeChat
这个项目readme虽然说只支持esp32,但esp32c2实测也是支持的。
似乎前面一个项目存在的问题都存在,只是功能似乎更强一些包括:
1、增加了本地ssid扫描和选择,根据界面提示是模组扫描的结果,但扫出来的wifi数量明显不足。
2、可在配网同时发送自定义的数据。但在putty打印上没有显示。当然这也可能是固件没有处理的原因。

官方文档:
https://freertos.org/Documentation/02-Kernel/02-Kernel-features/02-Queues-mutexes-and-semaphores/02-Binary-semaphores
https://freertos.org/Documentation/02-Kernel/02-Kernel-features/02-Queues-mutexes-and-semaphores/03-Counting-semaphores

信号量semaphore词源

semaphore除了表示信号量,还有旗语的意思。组成包括:

  • sema- 语义、符号/sign 的含义。如语义化版本Sematic Version,语义学Semasiology
  • phor- 带来/bring, 如欣快的euphoric, 隐喻metaphor, 发光体luminophor

组合起来表示带来信号。

信号量的使用

B站上有个信号量机制讲解视频https://www.bilibili.com/video/BV18P41187NV
信号灯(交通灯)就是一种信号量,在不同方向的汽车要使用同一个交叉路口的时候, 在同一时间,信号灯只允许某一方向的汽车通行。
在FreeRTOS中, 获取一个信号量即take,释放一个信号量即give。获取信号量相当于这个任务来到了十字路口,如果没有获取到信号量他就在十字路口这里等着,直到等到了信号量再继续执行。释放信号量可以在获取信号量的任务本身中进行,也可以在中断和回调中进行。
binary信号量是指只能取0或者1,counting信号量可以取大于1的值。
综合来说,信号量起到了和JavaScript的promise、await类似的作用。take相当于await等待。give相当于在promise的fullfilled。等待信号量释放的过程,相当于是等待另外一个异步程序执行完毕的过程。和await类似,避免了JavaScript的回调地狱,将异步函数执行完毕后需要做的操作仍然放在本任务中而不是放在回调后面。
对于有限的资源,JavaScript需要自行去实现信号量的计数,而freeRTOS直接提供了信号量。
下面是esp-idf的ledc fade渐变调光的示例代码,截取了其中和信号量有关的代码,以便说明。部分代码顺序略有调整,但不影响实际的代码工作逻辑。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "driver/ledc.h"
#include "esp_err.h"
/*
* ....... 省略代码
*/
// 这是渐变调光结束后注册的用户回调函数。其中释放give了用于监控回调是否完成的信号量。user_arg在这个回调函数注册的时候传入了信号量。
static IRAM_ATTR bool cb_ledc_fade_end_event(const ledc_cb_param_t *param, void *user_arg)
{
    BaseType_t taskAwoken = pdFALSE;

    if (param->event == LEDC_FADE_END_EVT) {
        SemaphoreHandle_t counting_sem = (SemaphoreHandle_t) user_arg;
        xSemaphoreGiveFromISR(counting_sem, &taskAwoken);
    }

    return (taskAwoken == pdTRUE);
}

void app_main(void)
{
/*
* ....... 省略代码
*/
    // 在主程序中注册了计数信号量,数字为要调光的led的通道数量。最后一个参数零表示计数信号量的起始值为0,也就是没有任何资源。
    SemaphoreHandle_t counting_sem = xSemaphoreCreateCounting(LEDC_TEST_CH_NUM, 0);
    // 在这儿注册led调光结束的回调函数, 并且将计数信号量作为用户参数也传入比较函数中。 
    ledc_cbs_t callbacks = {
        .fade_cb = cb_ledc_fade_end_event
    };
    for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) {
        ledc_cb_register(ledc_channel[ch].speed_mode, ledc_channel[ch].channel, &callbacks, (void *) counting_sem);
    }
    while (1) {
        // 在主程序中会循环调亮调暗的过程。先调亮到LEDC_TEST_DUTY,再调暗到0
        printf("1. LEDC fade up to duty = %d\n", LEDC_TEST_DUTY);
        for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) {
            ledc_set_fade_with_time(ledc_channel[ch].speed_mode,
                                    ledc_channel[ch].channel, LEDC_TEST_DUTY, LEDC_TEST_FADE_TIME);
            ledc_fade_start(ledc_channel[ch].speed_mode,
                            ledc_channel[ch].channel, LEDC_FADE_NO_WAIT);
        }
        // 因为上面的渐变调光函数事实上会调用异步任务或者中断函数ISR执行,完全执行需要时间,所以这里先取走了所有的信号量。因为起始的信号量为零,需要等待所有调光完成回调函数释放信号量后才能继续执行。
        for (int i = 0; i < LEDC_TEST_CH_NUM; i++) {
            xSemaphoreTake(counting_sem, portMAX_DELAY);
        }
        // 下面又从最高的亮度调为零,与上面类似,通过take信号量的方式等待调光执行完成。
        printf("2. LEDC fade down to duty = 0\n");
        for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) {
            ledc_set_fade_with_time(ledc_channel[ch].speed_mode,
                                    ledc_channel[ch].channel, 0, LEDC_TEST_FADE_TIME);
            ledc_fade_start(ledc_channel[ch].speed_mode,
                            ledc_channel[ch].channel, LEDC_FADE_NO_WAIT);
        }

        for (int i = 0; i < LEDC_TEST_CH_NUM; i++) {
            xSemaphoreTake(counting_sem, portMAX_DELAY);
        }
/*
* ....... 省略代码
*/
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}