Use windows terminal swtich environments

Use windows terminal swtich environments

之前写了个关于Cmder的相同的文章

Cmder是Windows命令行终端的一个很好代替,不光默认支持Git,甚至集成了一些Linux的工具命令。
最近因为使用GitUI,Cmder在调整窗口大小后基本就会图像错乱不能用了。但是Windows Terminal没有这个问题。
又发现最近Windows Terminal的功能不断增强。也可以很简单配置各个profile的启动时预置脚本,感觉启动还比Cmder快。
所以又写了这篇。唯一缺点就是注册右键菜单不是很方便

Windows Terminal Profile 配置

Windows Terminal本身就支持多配置文件功能可以用于环境切换。如下图

  1. 拷贝现有的命令提示符配置,追加IDEA202233新的配置
  2. 在命令行中设置中,添加启动时的执行脚本,配置环境变量,这里就简单把IntelliJ202233的路径设置一下
  3. 还可以更改图标,方便区分环境。当然还可以选择不同外观。

picture

注册特定配置为资源浏览器右键菜单

关键是这一步让很多人不想用Windows Terminal。没有从软件中直接支持。
其实是很简单的东西。希望马上可以支持。
网上很多文章有介绍原理。都懂,但是拿到自己机器上的时候就要好好看看。一步一步改。
所以就做了下面的添加,删除脚本。Windows 11的用户可以无脑使用。
不过,请注意以下脚本仅在Windows 11中测试。

  1. 添加:指定配置名称,就可以添加到右键菜单。可以指定ico图标文件,默认为cmd图标(因为WT的exe会随版本更新而路径不一样)
  2. 删除:列出已经添加右键菜单的配置名称,输入配置名称即可注销右键菜单。通过匹配菜单名称(以”Open Terminal”开头)来确定,如果自行修改脚本,请注意

picture

picture

2024-01-02-UpdateBlogToHexo

由于网站好久没有维护,升级一下到Hexo的最新版

单页App - 测试嵌入Markdown

单页App - 测试嵌入Markdown

测试页面 - AriaNg客户端

由于浏览器安全限制,https协议加载页面(Github Page)无法加载http协议。其他OnePageApps也会有同样限制。

单独画面显示

Sesame3スマートロック - もっと簡単にロック解除

Sesame3スマートロック - もっと簡単にロック解除

セサミ というスマートロック

本文はなぜ日本語で書くのは一つ主な原因はSesame3は主に日本と台湾で発売されてます。とても安価で使いやすいスマートロックです。
私は初代セサミスマートロックから使い始め、なぜセサミを選んだのは

  • 日本の電気錠もあるが、スマートではない
  • 日本のスマートロックは高値で、月課金のものもあった。あり得ない
  • マンションお住まいのため、統一されたドアの筐体とロックの制限があった。
  • 日本のGoldやMiwaのロックはなかなか欧米のスマートロックが対応されていない

その時セサミが現れました。良い商品です。初代は操作が競合したりなどの不便があったが、普通のロックに自動ロック機能や、リモートロックなどできるのは相当便利です。特に子供鍵忘れや、おばあちゃんが来るとき鍵忘れの時何回も活用しました。後やはり年が取るとドアがロックしたかしていないか。出かけた後結構悩む時があるので、これがあるとリモートでドア状態確認可能になりました。

最近セサミ3を購入し、アンロックスピードが早くなり、さらに快適です。

セサミ WebAPIとは

初代セサミ時代からWifiモジュールが販売されて、WebAPIを利用して、ロック・アンロック可能です。いろいろ面白いインテグレーションが可能です。Googleアシスタントや、Siriを便利に使えました。

しかし、セサミ3が発売されたとともに、初代のDashboardが使えなく、初代のWebAPIも使えません。新しいAPIとなりました。またセキュリティ向上されている可能性があり、WebAPIの利用が元のような単純なものではなく、一定の暗号化処理が必要になってきました。

セサミAndroid Appの問題

(最近自分がメインにAndroidで運用していて、IOSのことあまり気にしていませんが…)

セサミ3のAndroid Appを利用していますが、自動アンロック機能がずっと通知欄にWidgetがでばなしです。バックグラウンドで位置サービス監視が必要という裏原因も理解しますが、やはり気に入らなく、NFCタグを試しました。

NFCタグを試したところ、さらにがっかりしました。Appをオープンしないと、開錠できません。
しかも単にあるタグの標識を記録するだけで、Appが閉じたままでタグをスキャンすると、まずシステムでApp起動の選択になります。
自分でちょっと工夫して、NFC TagWriter by NXPを利用して、NFCTagでAppを起動するようにしたら、実際操作して見たところ、2回のスキャンが必要で、間隔時間が必要です。(さらにイライラ…)

どうすれば一回のタッチで開錠できるでしょう

理想論:スマホの状態が問わず、1回スキャンしただけで、開錠する

# アプローチ メリット デメリット
1 AppでWebAPIを利用して開錠操作する 開発簡単、仕組みシンプル Wifiモジュールが必要、スマホアンロック操作が必要
2 AppでSesameSDKを利用して開錠操作する Wifiモジュールが不要、仕組みシンプル 開発難しい、スマホアンロック操作が必要
3 スマホのNFCシミュレーター機能で、NFCTagをシミュレーションし、カードリーダー+専用サーバで開錠操作する スマホロックしたままで使える、自分の暗号化内容をTagに仕込めば、簡単にNFCTagを複数作り出し、子供に配布可能、公式サンプルソースをそのまま利用可能 カードリーダーを動作させるおよびWebAPI機器が必要、ドアに設置する工夫が必要(電源どうするなど)

#3のドアに機器の増設を考えるだけで頭いっぱいです。このオプションは本当にセサミの製造会社に考えてほしいです。スマートロックと同様、バッテリー1年持つのカードリーダー+Wifiモジュールでできるではないかと思います。(今スマホAppのNFC機能は本当に微妙、特にAndroidのAppがいまいちです。ISOは直接開錠操作するショートカットがあるので、まだ良いかも)

自分の条件に合わせて、やはり#1のは一番現実的です。

現実論:スマホアンロック状態で、1回スキャンしただけで、開錠する

これをよく考えると、AppのNFC機能不要ではないか、WebAPI機能を持てば、NFCTagによる起動か、ランチャーアイコンによる起動か区別できれば、NFCTag起動時WebAPIを実行すればよいです。

では、開発:WebAPIをJavaで利用するの難点

参考文献→ブログ

文献で書いたものを重複せず、実際あった問題と解決コードを載せる

  • Nodejsではなく、Javaの場合、URLDecodeがとても重要です。最初はこちらに嵌めて、Base64のデコードがうまくいかず…
    下のscanStrは文献・ブログに記載があったSesameの共有QRコードからスキャンした文字列です。
    下記コードはQRコードから読み取った情報(設備名、UUID、秘密キーを含めて)を解読しました。
    そのため、私のコードを利用する際、秘密キーを気にする必要がなく、QRコード情報+APIKeyでOKです。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    private void init() {
    String[] paramParts = getParamParts(this.scanStr);
    HashMap<String, String> paramMap = getParamMap(paramParts);
    this.dataType = paramMap.get("t");
    this.permission = paramMap.get("l");
    this.name = paramMap.get("n");
    String sk = paramMap.get("sk");
    byte[] skBytes = Base64.getDecoder().decode(sk);
    this.deviceType = bytesToHex(Arrays.copyOfRange(skBytes, 0,1));
    this.secretKey = bytesToHex(Arrays.copyOfRange(skBytes, 1,17));
    this.publicKey = bytesToHex(Arrays.copyOfRange(skBytes, 17,81));
    this.keyIndex = bytesToHex(Arrays.copyOfRange(skBytes, 81,83));
    this.uuid = bytesToHex(Arrays.copyOfRange(skBytes, 83,99));
    }

    private HashMap<String, String> getParamMap(String[] paramParts) {
    HashMap<String, String> paramMap = new HashMap<>();
    for (String paramPart : paramParts) {
    String[] keyVal = paramPart.split("=");
    if (keyVal.length != 2) {
    throw new RuntimeException("QR code information is not correct!");
    }
    paramMap.put(keyVal[0], keyVal[1]);
    }
    return paramMap;
    }

    private String[] getParamParts(String uri) {
    String[] uriParts = uri.split("\\?");
    if (uriParts.length < 2) {
    throw new RuntimeException("QR code uri information is not correct!");
    }
    try {
    String paramUrl = java.net.URLDecoder.decode(uriParts[1], StandardCharsets.UTF_8.name());
    return paramUrl.split("&");
    } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
    throw new RuntimeException("QR code uri information is not correct!");
    }
    }

    private String bytesToHex(byte[] bytes) {
    char[] hexChars = new char[bytes.length * 2];
    for (int j = 0; j < bytes.length; j++) {
    int v = bytes[j] & 0xFF;
    hexChars[j * 2] = HEX_ARRAY[v >>> 4];
    hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars);
    }
  • generateRandomTagのロジックのJava書き換え:正確にByte切り出し部分の理解とCMAC暗号化の実装
    ここは難しいから、一々Nodejsの実行内容をデバッグで比べながら進めました。結果的に動けましたので、それ以上語る内容はございません(^_-)-☆。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    public String generateRandomTag(){
    // 1. timestamp (SECONDS SINCE JAN 01 1970. (UTC)) // 1621854456905
    long timestamp = new Date().getTime() / 1000;
    // 2. timestamp to uint32 (little endian) //f888ab60
    ByteBuffer buffer = ByteBuffer.allocate(8);
    buffer.order(ByteOrder.LITTLE_ENDIAN);
    buffer.putLong(timestamp);
    // 3. remove most-significant byte //0x88ab60
    byte[] message = Arrays.copyOfRange(buffer.array(), 1,4);
    return getCMAC(parseHexStr2Byte(secretKey), message).replace(" ","");
    }

    private byte[] parseHexStr2Byte(String hexStr) {
    if (hexStr.length() < 1)
    return null;
    byte[] result = new byte[hexStr.length()/2];
    for (int i = 0; i < hexStr.length()/2; i++) {
    int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);
    int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);
    result[i] = (byte)(high * 16 + low);
    }
    return result;
    }

    public String getCMAC(byte[] secretKey, byte[] msg) {
    CipherParameters params = new KeyParameter(secretKey);
    BlockCipher aes = new AESEngine();
    CMac mac = new CMac(aes);
    mac.init(params);
    mac.update(msg, 0, msg.length);
    byte[] out = new byte[mac.getMacSize()];
    mac.doFinal(out, 0);

    StringBuilder s19 = new StringBuilder();
    for (byte b : out) {
    s19.append(String.format("%02X ", b));
    }
    return s19.toString();
    }
  • Rest APIリクエストの実装(できるだけ小さいAppサイズにしたいため、サードパーティJar利用せず)
    これは多くの人が調べればかけるもので、一応ソリューションとして記載します。generateRandomTagや、Historyなどの値設定方法を示します。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    private int executeCmd(String cmdStr) {
    String base64History = Base64.getEncoder().encodeToString("NFC Unlock".getBytes());
    String sign = generateRandomTag();
    String json = String.format("{\"cmd\": \"%s\", \"history\": \"%s\",\"sign\": \"%s\"}", cmdStr, base64History, sign);
    try {
    URL url = new URL("https://app.candyhouse.co/api/sesame2/" + this.deviceId + "/cmd");
    HttpURLConnection con = (HttpURLConnection) url.openConnection();
    con.setRequestMethod("POST");
    con.setRequestProperty("Content-Type", "application/json; utf-8");
    con.setRequestProperty("x-api-key", apiKey);
    con.setDoOutput(true);

    OutputStream os = con.getOutputStream();
    byte[] input = json.getBytes("utf-8");
    os.write(input, 0, input.length);
    os.close();

    BufferedReader br = new BufferedReader(
    new InputStreamReader(con.getInputStream(), "utf-8"));
    StringBuilder response = new StringBuilder();
    String responseLine = null;
    while ((responseLine = br.readLine()) != null) {
    response.append(responseLine.trim());
    }
    System.out.println(response.toString());
    br.close();
    int code = con.getResponseCode();
    con.disconnect();
    return code;
    } catch (IOException e) {
    e.printStackTrace();
    return 400;
    }
    }
  • その他:Android/HarmonyOS実装で、UI Threadとのやり取りの非同期実装
    UI Threadと時間がかかるネットワーク処理の同期・非同期処理が一番理解に時間が掛かりました。自分は初心者ですが、下記は自分的の答えです。同期Executeと非同期Executeを両方用意し、非同期の場合、Callbackを用意しています。同期処理はメインThreadでTimeout待機し、WebAPIの応答コードを返すことです。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    public class SesameCmd implements Runnable, Callback {
    public SesameCmd(
    QRCodeInfo info,
    String apiKey,
    String cmd) {...}

    @Override
    public void run() {
    int code = executeCmd(cmd);
    if (c != null) {
    this.c.callback(code);
    }
    }

    public void executeCmdAsynchronously(Callback c) {
    this.c = c;
    new Thread(this).start();
    }

    public int executeCmdSynchronously() {
    this.c = this;
    int timeout = 100000;
    new Thread(this).start();
    try {
    while (responseCode == 0 && timeout > 0) {
    Thread.sleep(1000);
    timeout = timeout - 1000;
    }
    return responseCode;
    } catch (InterruptedException e) {
    e.printStackTrace();
    return responseCode;
    } finally {
    this.c = null;
    this.responseCode = 0;
    }
    }

    @Override
    public void callback(int code) {
    this.responseCode = code;
    }

    private int executeCmd(String cmdStr) {...}

    public String generateRandomTag(){...}

    private byte[] parseHexStr2Byte(String hexStr) {...}

    public String getCMAC(byte[] secretKey, byte[] msg) {...}
    }

結局Appで何を実現したか…

  • QRコードをスキャンして、直接すべての情報を保存します。
  • APIKeyはユーザご自身で取得し、入力してテストする機能、テストで成功すると、自動的に保存されます。
  • アンロック・ロック操作すると、結果画面が表示され、2秒後自動的にApp終了します。
  • ランチャーアイコンで入ると、設定画面、NFCで起動する場合設備情報が十分であれば、スマートロック動作させる、でなければ設定画面へ
  • 設定完了後、NFC TagWriter by NXPでApp起動Tag書き込みで、com.giko.sesamenfcを書き込めば、NFCTag作成完了。このTagで動作させることが可能です。

picture

一応QRコードがある+APIKeyがある前提のため、セキュリティ的にAppは一般ユーザでも使える状態になります。
また裏で変な情報別のサーバに送信しているかいないか怪しい疑われるかものため、
全量ソースは下記に公開しています。利用するオープンソースはあるが、みんなGoogleなど実績があるものになります。
https://github.com/yougikou/NFC4Sesame

2021/11/06 Harmoney OSはIOSのような仕組みで、NativeのHarmonyOSのAPPの自由配布ができません。そのため、Android版を作り直しました。
https://github.com/yougikou/QuickSesame

これがあれば、Androidのスマートウォッチや、HarmonyOSの軽量スマートウォッチ(GT2 Proなど)でも操作可能になります。
(次にチャレンジしてみます)

動作してみるイメージは下記のようです。

2回スキャンのイライラ感が軽減ですます。

IMAGE ALT TEXT HERE

著者権を気にしていて、アイコンも自分で作りました。

まとめ

セサミのAppにも上記アドバイスをしましたが、うまく解決してくれるかどうか微妙です。
日本はやはりiPhone大軍がメジャーかも。
でもこのぐらいの努力で、Androidユーザ・HarmonyOSユーザが少しでも良い製品を生活で便利に使えたら、嬉しいことだと思います。
後ほど日本語化をもっと完璧にやろうかと思います。今は中国語でハードコーディングしています。

後続スマートウォッチで操作可能にしたいが、いつもデカいスマホを取り出すのは不便です…
セサミの公式開発はまずやってくれることを期待したいが、我慢できなければやってしまうかも。

興味・質問があったら、コメント残してね

ReactIndex - 让文件夹index页面变得更有实用性

ReactIndex - 让文件夹index页面变得更有实用性

React的学习和家庭需求

 工作上有需要接触React,而在正式接触相关产品之前为了了解一些基础。除了标准教程里面的例子意外,总觉得有点不够。
正好孩子他妈最近交给我一个任务:

  • 儿子的学习资料需要整理,同时要方便他复习。
  • 复习的时候最好可以迅速查看,答案最好就在旁边,方便反复记忆。

需求分析和精炼

针对领导的要求,本人第一时间想出

大致方案

  • Web方式的学习资料浏览最大限度的提高了可用性。只要自建服务器,随时随地可以让孩子利用空余时间浏览。
  • 答案切换 - 简单,用Javascript做个简单的图片切换即可。
  • 大部分图像链接使用python自动生成html页面。

说动手就动手,结果初步方案尝试下来发现几个问题

  • 图片切换的JavaScript很简单,但是生成静态页面即便使用python自动化,也还是需要手动执行。文件的维护不仅仅只是图像文件本身。
  • 打开页面还是需要从Directory index进入,对于大量文件夹,多少需要分类的功能。

实现方案方向变更

  • 由于Directory index的形式就是固定的。使用ajax获取后可以进一步处理。生成动态页面,实现仅仅需要维护文件夹中的内容文件即可。
  • 使用react构筑,添加内容分类功能。

React index的雏形

使用Ant design替换了简陋的Directory index

picture

加入基于文件夹名的二级分类功能

picture

加入文件夹内容类型设定添加Markdown文本页面(初步尝试)

picture

添加学习资料复习页面-使用Ant design组件显得有逼格

picture

不是很需要-但是还是加入基于在线iconfont的分类图标自定义功能

picture

后续计划

React Index的设计本身就是用于家庭成员简单查看的家庭内部网站。
只要懂得文件夹、文件的组织管理,妈妈孩子都可以很容易的添加内容。
所以针对现在家庭使用中的一些需求,有一些后续打算

  1. 添加(不用下载PDF的)PDF预览(这个还需要技术调查,好像有js库可以实现)。
  2. 多域名支持和切换,这样可以配置多个(可能不是很有必要)。
  3. 功能页面看看能不能做成动态加载,方便扩展。
  4. 看领导需求。。。
抛砖引玉 - 利用Cmder对本地Java运行时环境的切换应用

抛砖引玉 - 利用Cmder对本地Java运行时环境的切换应用

多版本环境

做开发的朋友大多数都会有多个环境的切换困扰。通常一个好的运行时环境能有一个好的版本管理器比较重要。
就譬如NodeJs的nvm - 虽然这也是因为Node的版本升级实在太快,不得不出这么一个(不然就是噩梦)。
但是相对来说向前兼容性比较强的Java环境来说,这种需求不是很突出。所以这类版本管理在Java环境中就不是很常用。

不过相对于比较滞后的企业应用,Oracle对JDK的不断升级迭代,很多旧系统不得不跟随步伐,
很多商业软件也不得不跟随步伐的情况下。JDK的多版本环境的需求很多时候也是刚性的。

Cmder

Cmder是Windows命令行终端的一个很好代替,不光默认支持Git,甚至集成了一些Linux的工具命令。
除了UI,对于Windows系统上下文菜单的集成配置自由度也是相当的高。

思路

在安装多个JDK需要切换的时候,通常就是在指定程序的启动bat里面临时替换JAVA_HOME和PATH。
非常简单但是每次都要写一段,有时候需要改变运行时还需要改动。简单但是繁琐。
由于Cmder本身带有一个初始化script。理论上把JAVA_HOME和PATH的替换命令加进去就可以了。

做法

原理很简单,但是如何在一个工具中实现理论上的配置通常就是比较花时间的地方。
而且工具也可能有多个地方可以更改,如何找到最优的方法也是比较花时间。
下面是我个人认为最优的方法。无需编辑添加任何文件-直接在设置中添加。

  1. 打开Cmder设置,点击进入【启动-任务】设置

  2. 复制一个现有任务(以管理员打开cmder比较好),加上适当后缀,譬如jdk7

  3. 修改命令组内容,如下
    picture

    以下文本用于复制粘贴

    1
    *cmd /k %ConEmuDir%\..\init.bat&SET JAVA_HOME="C:\Java\jdk1.7.0_25"&SET PATH=C:\Java\jdk1.7.0_25\bin;%PATH%
  4. 点击进入【集成】设置

  5. 选取一个菜单项(譬如Cmder Here)后,更改菜单项名称(譬如 Cmder Here w/jdk7),命令(使用新建的jdk7任务),然后点击【注册】
    picture

确认

这样你会发现在Windows系统的上下文菜单(右键菜单)中多了一个【Cmder Here w/jdk7】。
在任意文件夹中右键点击该项,就会启动一个cmder的命令行窗口。
picture
输入java -version 就可以确认到你预先设置好的Java环境了
picture

应用和TODO

上述是利用Cmder最基本的【在文件夹中打终端】功能,实现在特定PATH下指定运行时打开命令终端。

  • 同理可以应用到任何一种基于PATH的运行时环境的切换。
  • Cmder还可以把选中文件作为参数传入。这个我还没尝试成功如何设置。
    理论上应该可以指定Java JDK版本运行选中bat。加上这个功能的话用着就更加舒服了。
    有朋友知道的话,希望分享一下设置方法,命令组内容。
坑人的Windows 10上网限速/带宽变慢问题

坑人的Windows 10上网限速/带宽变慢问题

缓慢的网速

通常上网速度缓慢我都会归咎于我的垃圾网络供应商以及住宅的设备限制,只能上一般的百兆光缆,而且一到晚上上网高峰时段就会出现龟速。
但是理性思考一下我会考虑一下几点:

  1. 供应商地区通信高峰(想象)
  2. 同时段使用高峰(想象)
  3. 路由器过热导致通信不畅通(想象)
  4. 线材不好或者WIFI中有干扰(想象)

但是我万万没有想到,Windows系统会在一定的条件下出现限制网速的情况。
(…通常一个坑,是通过很长时间难以解开的心结来让人坚持爬出来的)

我买了台Nvidia的Shield TV 2019
picture

虽然我主要为了4K,而不是游戏,但是终究忍不住传说中的串流(Stream),特别是我免费获取了GTA5之后。
于是

  1. 串流主机配置 - OK!(Lenovo Game PC)
  2. 速度测试 - OK!
  3. 游戏安装 - OK!
  4. 串流开始!…… 图像开始变渣……网络过慢,断线!!!

啊!!!啊!!!
picture

过后持续一两周,每次抱着侥幸心理打开来试一试,看看会不会突然顺畅了。
每次都是徒劳。直到一个周末…
(由于我的家用游戏电脑使用率不是很高,对于网速这点其实并不敏感)。

我一大早打开个网页…好慢…

Netflix测速功能…5-10Mbps…

是不是疫情期间大家都在家,网速慢了(惯性思维)。

闲来无事,打开路由器页面看看IPv6连接信息有没有变化

…路由器页面…好慢…

嗯…用路由器测速功能测一下吧

…80-85Mbps…哈?

我的这台机器慢!!?

第一次失败尝试:
由于是单机原因,首先管理员执行

1
netsh winsock reset

Netflix测速 - 80Mbps复活
那我的Nvidia Stream也有戏了吧。马上测试!

  1. 速度测试 - OK!
  2. 串流开始!…… 图像开始变渣……网络过慢,断线!!!

嗯…不开游戏…80Mbps
开游戏…10Mbps
不开游戏…80Mbps
开游戏…10Mbps

啊!!!啊!!!肿莫回事!!!
picture

敢情一开始的网速过慢是哪次关游戏的时候机器没有缓过气来?…
还有这种狗血原因!

查资料 - 四处Google解决方案

由于这类信息乱七八糟的太多,避开曲折不谈。直击重点Win10网速,解除限速步骤:

  1. 按 Win + R 组合键,打开运行,并输入:gpedit.msc 命令,确定或回车,打开本地组策略编辑器
  2. 本地组策略编辑器窗口中,依次展开:计算机配置 - Windows 设置,然后在右侧找到并右键点击基于策略的 QoS,在打开的菜单项中选择高级 QoS 设置
  3. 高级 QoS 设置窗口,切换到入站 TCP 流量,勾选指定入站 TCP 吞吐量级别,并选择级别3(最大吞吐量),最后点击确定
  4. 重新返回到本地组策略编辑器窗口中,依次展开:计算机配置 - 管理模板 - 网络,然后在右侧找到并双击打开QoS 数据被计划程序
  5. 接着在Qos数据包计划程序下找到并且双击打开限制可保留带宽
  6. 限制可保留带宽,并将下方选项的带宽限制修改为0,并确定保存

重新测试Nvidia Stream

Netflix测速 - 80Mbps复活!!

Nvidia Stream测试 - 复活!!!开游戏网速也不会变慢了。
这下又再次坚定了我购买蓝牙手柄的决心。虽然可能积灰的时间比较多。

picture

网络上写解决方案的作者也说 - 不知道是不是有用,但是很可能会变快。

而这次经历再次证明,人们总以为自己掌握了一切,但是其实却一无所知。

保持一颗虚心求学的心,自始至终都是如此重要。


重装Windows后追加: 最终对于这个问题的终结

在过后几次的使用中,最终这个问题会不断复发。
原因是大多数游戏平台客户端(EpicGame,Battle.net)在游戏下载时都会进行限速,
这在动辄100GB的游戏,下载速度8MB-10MB/s的情况下,也情有可原。
但是不知道什么原因,本来下载安装游戏时的系统策略会在玩游戏的时候也产生影响,
造成了非常悲剧的结果。

万般无奈之下刚好Windows进行了2020 May更新,从云端纯净重置系统变得很简单。
在30分钟的重装完成,1小时左右的环境设置后,再进行多次尝试,总算是恢复正常。

总结一下:
上述命令以及策略修改的确是会产生一定效果。但是如果根本原因没有找到。
估计大多数情况下还是会复发。而重装也证明正常情况下系统设置是不需要用户干预的。
所以平时尽量把资料保存在不同盘符,日常处理也尽量使用云服务。
这样在万一的时候可以轻松重装系统,是百试百灵的良药。


Python PDF自制工具

Python PDF自制工具

方便的快速编程语言和丰富的扩展

 我对于Python的印象就是一个好写, 好除虫, 快速上手的强大的批处理工具语言.当然由于工作上不是经常用到,所以认识比较浅薄.
这阶段由于为了帮孩子有效复习, 买了可以双面扫描的打印机后, 发现家用打印机毕竟不是商用, 一些功能还是比较不方便.
譬如, 扫描下来的页面只能是竖着的, 电脑上查看要横过来还得自己旋转. 有些是书本册子的, 中间的装订线去除后扫描, 页面需要分割, 顺序也需要重新排序. 虽然有PDF编辑软件, 但每次这种单纯操作的工作量还是很大.
而且很狗屁的是很多所谓的免费PDF编辑软件都一些基本功能, 还要安装. 一旦需要高级一点的马上需要升级订阅.
这时我首先想到了Python.

使用PDF库

 pdfrw - 至少对于我的需求这个库非常的好. 一开始使用PyPDF2, 但是在分割的时候发现会造成文件大小翻倍, 也不能压缩,所以途中放弃转用pdfrw. 而且发现该作者还很热心, 在PyPDF2的分割文件大小翻倍的issue thread上给出pdfrw的写法.

自制工具

 所有工具追求易用, 都采用选择PDF, 拖拽带相应的py上后就开始执行.
所有输出统一到同文件夹内新建out的子文件夹中.
出现任何异常console画面会停顿10秒可以查看出错的命令行数, 错误内容.

使用前先安装pdfrw(pip install pdfrw).
最好把Python升级到3.8以上, 我发现有新的python launcher可以自动关联py文件.
双击, 拖拽文件执行都很方便.

  1. mergePdf.py
    把拖拽的多个文件合并成一个文件. 拖拽时鼠标拖动的文件最好是第一个.
    不然可能点中的文件可以跑到头上去.
  2. rotateL90.py
    把拖拽的多个文件都执行向左旋转90度
  3. split&sort.py
    这个比较高级点. 是根据以下的扫描前提进行处理的.
    去装订线扫描时 - 从中间页(为第一页)开始扫描.
    这样譬如中间页是 11,12, 则 Page1(11,12), Page2(13,10), Page3(9,14)…
    所以这个py把拖拽的文件都执行下述操作后分别输出为一个新的pdf.
    原文件可供制册打印, 处理后的文件可用于电脑阅读.
    • 分页: 把每页分成左右两页
    • 排序: 根据上述规则, 把分割的页面按真正的页序排序

2020-05-05追加

  1. split&sort_reverse.py
    上一个脚本的小改动, 以应付竖版装订书的页序(像古文那样从右往左读,翻页的册子)
  2. reverse.py
    单纯倒序排列PDF页面.
  3. delete.py
    删除指定页面, 仅处理单个文件. 删除页面以空格分隔指定.

感觉有了python都懒得打开臃肿的PDF编辑软件了.

分享py以供有同样需求的朋友, 或者当样例参考, 毕竟需求类似的话比自己从0开始考虑要省力很多.

Git - 如何在Windows git环境下提取两次commit的文件增量并打包成zip

Git - 如何在Windows git环境下提取两次commit的文件增量并打包成zip

背景

工作上用了一段时间的 Git, 虽然同事都推荐直接用命令行, 无奈我这个人还是喜欢看 UI. 而且在 SVN 时代, 抽取两次提交的变更文件是非常惯用的操作, 切换到 Git 之后一直为如何实现这个命令耿耿于怀. 虽然看了有各种方式可以实现, 但是始终没到自己惯用的方式.
最近发现新思路, 于是就整理一下, 方便大家 Google.

一些通常能 Google 到的方法

  1. TortoiseGit 内建了一个在 log 画面中选择文件后抽取的功能
    TortoiseGit Export Selection To...
    这个功能对于抽取单次提取的文件很方便, 但是多次累计就用不上了, 在 log 画面选两个 commit 完全和 svn 不一样干脆就看不到文件了. 虽然网上有文章介绍这个小技巧, 但是因为这个原因, 所以…不完美.

  2. Windows batch
    我曾一度使用, 是一个日本人写的. 原理就是用 git diff 出来的结果放入循环, 一个一个拷贝出来.
    可以设置到 SourceTree 的 Custom Actions 中直接使用.
    这个人的 blog 我现在也找不到, 就不贴了. 而且这个方法有个弊病!
    在 Windows 环境下, 文件路径是有长度限制的, 在很多项目中文件夹太多层的话, 就会出现找不到文件的问题.
    当然从 Windows 10 开始通过策略组可以取消这个限制 - 但是也就证明了在环境不通用的情况下不是每个人都能成功配置,所以…不完美.

  3. Shell script
    Git diff $sha1 $sha2 –name-only | xargs tar -zcvf update.tar.gz 这个方法是命令行, linux 中最通用的.
    的确很好使, Windows 环境下安装 Git 自带的 Linux 工具命令后, 使用起来一点问题没有.
    但是就是每次得拷贝 SHA 值比较费神, 另外 tar 的压缩方式在 Windows 下总觉得别扭, 解压得 2 次…不完美

  4. SourceTree 原生的功能就更不用提了, 只能 Archive 真个项目状态, 太费事费力.

几个关键以及解决方式

  1. 如何从 GUI 操作传入选中的 SHA 值
    SourceTree 的 Custom Actions 提供了这点, 可以传递$SHA 给 Script.

  2. 如何让 SourceTree 的 Custom Actions 执行 bash 的 shell scrip
    这个点是最重要的关键, 但是我在找这个解决方案的时候, 没有文章提及这点, 还是瞎猫碰死耗子在一片完全不相关的文章中找到了答案. 配置如下, Git 是大家都会装的东西, bash 自动附带.
    SourceTree Custom Action Run Bash Script

  3. 这样我们就可以在 Windows 下用方便的 shell command 抽取两次提交的变更文件, 同时打包.
    但是还有一个问题就是如何打包成 zip, Windows 自身没有带这方面的命令. 而 Git 的 linux 工具包 unzip, tar 都带了, 唯独 zip 这个东西没带. (我在调查这个的时候有段小插曲, 我两台电脑, 一台有 zip 命令, 一台没有. 一开始我搞不懂我装了什么不一样的东西造成了这个差异, 花了好长时间才发现 Oracle 数据库安装目录的 bin 里面居然带了 zip.exe…好吧)

    关键我在网上除了找得到这个项目(Info-ZIP)的代码, 居然找不到编译好的 zip.exe,拷贝来用. 有需要的朋友可以直接在这里下(zip.exe.zip).
    放到 Git 安装文件夹的那个 linux 工具包路径(Git\usr\bin)里就可以像其他命令一样直接使用.

    下面就是 shell 本身, 其实搞明白就没啥大不了的. 有需要就参考一下.

    $Repo 不是必须的, SourceTree 中执行时当前文件夹自动为项目根目录.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # use parameter -> $REPO $SHA
    repo=""
    sha1=""
    sha2=""
    if [ -n "$1" ] && [ -n "$2" ] && [ -n "$3" ] && [ -z "$4" ]; then
    repo=$1
    sha1=$3
    sha2=$2
    echo "Repository is ${1}."
    echo "Current path is ${PWD}."
    echo $2
    echo $3
    # git diff --diff-filter=ACM $sha1 $sha2 --name-only | xargs tar -czvf update.tar.gz
    git diff --diff-filter=ACM $sha1 $sha2 --name-only | xargs zip -r ./update.zip
    else
    echo "please select two commit"
    fi
    exit

补充: 添加–diff-filter=ACM 把变更限定为添加更改的文件, 因为删除的文件在打包时自然会找不到. 可以看 log 或者添加这个 option 事先除去.

不知道向我这样还是用 Windows 做主要开发环境, 又有上述需求的朋友是否很多, 我找了很久找不到一个完美方案, 这个方法是自我觉得比较完善的, 给有需要的朋友参考.
十多年没码字, 写这么一点就有点累了:-O