2021年12月31日 星期五

how is the routing table populated and used in a router?

路由器屬於開放系統互連 (OSI, Open Systems Interconnection) 模型第3層設備,認得IP住址,負責跨網段的封包繞送 (routing)。其繞送依據路由器內的路由表 (routing table),內含依序由緊到鬆的多條 路由 (route),每條路由指明封包去處IP若符合某 目的網段 (destination network),可由路由器何 出口 (exit) 送出,以儘早到達目的地。

路由表的路由分成 靜態路由 (static route),及 動態路由 (dynamic route) 兩種。動態路由須由網域管理者開啟同一套 路由協定 (routing protocol),透過路由器彼此交換路由資訊,收斂 (convergence) 後學得。動態路由好處可自動因應網路拓樸變化,適用於大型網域路由管理,缺點為路由收斂須消耗資源及時間。靜態路由則由路由器管理者設定而得,好處是省下路由學習的資源及時間,缺點是無法自動因應網路拓樸變化,適用於小型網域管理。

路由表最後一條路由稱為 預設路由 (default route),凡是無法匹配路由表前頭路由的去處 IP 住址,皆由此通吃靜態路由決定轉送出口。因此,網域 尾端網段 (stub network) 常將其路由器的預設路由設定為網段對外出口,遇到目的地不在尾端網段內的封包,一律往網段對外出口轉送。

how are wireless local area networks (WLAN) used in an enterprise network?

無線區域網路 (WLAN, Wireless Local Area Network) 為 IEEE 802.11 無線通訊標準,使用 2.4 GHz 及 5 GHz 的無線頻道,適合距離約 100 公尺範圍的家庭、辦公室、校園環境之用。WLAN 使用載波感應多重存取與避免碰撞 (CSMA/CA, Carrier Sense Multiple Access/Collision Avoidance) 技術,判斷何時及如何在無線網路上傳送資料。WLAN 提供隨意 (ad hoc),共連 (tethering),基礎設施 (infrastructure)三種通訊模式。基礎設施模式下,眾多客戶設備以無線方式聯結 (associate) 到骨幹存取點 (AP, Access Point),再利用有線骨幹設施通往上游。無線用戶端聯結 AP 存取點過程,可以採用 被動 或 主動掃描(探測)方式。採用被動方式時,AP 須定期廣播發送包含服務區代碼 (SSID, Service Set Identifier)、支援的無線標準、安全組態等服務公告,供用戶端選用。

無線區域網路控制器 (WLC, Wireless Lan Controller) 提供網頁設定畫面,可利用 IEEE 制定的無線存取點控制及部署協定 (CAPWAP, Control And Provisioning of Wireless Access Points),管理多個 AP 存取點和無線區域網路。CAPWAP 協定源自輕量存取點協定 (LWAPP, Lightweight Access Point Protocol),增添了資料片傳輸層安全 (DTLS, Datagram Transport Layer Security) 機制,可建立加密的資料片(UDP, User Datagram Protocol) 通道,於 AP 和 WLC 之間傳送訊息。

    Reference:
  1. CCNA2: 12 WLAN Concepts
  2. CCNA2: 13 WLAN Configuration

2021年12月18日 星期六

how do hosts get a reliable connection settings in an IPv4 or IPv6 network?

動態主機組態協定 (DHCP, Dynamic Host Configuration Protocol) 為一種客戶設備向網段伺服器詢問連網組態的協定。DHCPv4 經過探索,提供,請求,確認四步驟,可為客戶設備提供完整 IPv4 連網組態,包含自己的 IP,遮罩,對外閘道 IP,DNS 伺服器 IP 等資訊。

在 IPv6 環境,主機介面可自動生成鏈路本地住址 (LLA, Link Local Address),但僅供和本地路由器交換訊息,無法對外使用。至於完整 IPv6 連網組態,包含可對外使用的全域單點傳送位址 (GUA, Global Unicast Address),前置碼 (Prefix) 長度,對外閘道,DNS 伺服器等,則須主機接收網際網路控制訊息協定 (ICMPv6, Internet Control Message Protocol) 的路由器公告 (Router Advertisement),依其建議取得。

主機利用無狀態住址自動設定 (SLAAC, StateLess Address Auto-Configuration) 協定可取得 IPv6 GUA 住址。其中,無狀態取得之住址並無統一管理,可分成純 SLAAC 取得法,及 SLAAC 加無狀態 DHCPv6 取得法。有狀態取得之住址受統一管理,即所謂有狀態 DHCPv6 取得法。不管是有狀態或無狀態取得之住址,主機都會利用重複位址偵測 (DAD, Duplicate Address Detection) 機制,確保取得的 IPv6 GUA 唯一,不和網段內其他主機重複。遇網段內無 DHCPv6 伺服器,路由器可開啟 DHCP 轉送代理 (Relay Agent) 功能,提供 DHCP 轉送服務。

首站備援協定 (FHRP, First Hop Redundancy Protocol) 可建立區網對外虛擬閘道,其背後對應多個實體對外閘道,能避免單點故障影響對外連線,提升區網對外可靠性。熱待機路由器協定 (HSRP, Hot Standby Router Protocol) 為思科專屬 FHRP 協定,思科另有閘道負載平衡協定 (GLBP, Gateway Load Balancing Protocol)多提供負載平衡能力。至於開放的 FHPR 協定則有虛擬路由器備援協定 (VRRPv2/v3, Virtual Router Redundancy Protocol)、ICMP 路由器查找協定(IRDP, ICMP Router Discovery Protocol)等。

    Reference:
  1. CCNA2: 07 DHCPv4
  2. CCNA2: 08 SLAAC and DHCPv6 Concepts
  3. CCNA2: 09 FHRP Concepts

2021年12月13日 星期一

why the spanning tree protocol (STP) is needed in a higher-end switch?

延伸樹協定(STP, Spanning Tree Protocol)為一種中階交換器功能,允許多部交換器串接而不會形成迴路,造成廣播風暴。其原理為將交換器之間連線形成的圖形拓樸透過學習轉換成樹狀拓樸,讓任兩部交換器之間只有1條路徑相連,避免形成迴路。樹狀結構的學習過程會用到OSI模型第2層的橋接協定資料單元 (BPDU, Bridge Protocol Data Unit) 訊框,彼此交換資料,最後收斂決定:哪部交換器要當根橋接器 (root bridge),各交換器的骨幹埠 (trunk port) 看是要成為連向上游的根連接埠(root port),連向下游的指定連接埠(designated port),或暫時關閉的備用連接埠 (alternate port),大家講好以免形成迴路。

STP協定有諸多改良版本,例如思科專屬的增強型逐個虛擬區網生成樹 (PVST+, Per-VLAN Spanning Tree Plus), Rapid PVST+。另外,亦有公開的IEEE快速生成樹協定 (RSTP, Rapid Spanning Tree Protocol) 提供比 STP 更短的學習收斂時間;IEEE多重生成樹協定 (MSTP, Multiple Spanning Tree Protocol) 則可將多個 VLAN 生成樹 由少數幾個運算個體負責,節省運算資源。

交換器允許設定存取埠 (access port) 啟用 PortFast 加速功能,直接接受終端設備訊框,加快存取速度;亦可設定存取埠啟用 BPDU Guard 防護功能,遇到設備送來BPDU訊框時,關閉該連接埠,避免形成迴路。

    Reference:
  1. CCNA2: 05 STP

2021年12月12日 星期日

why virtual local area networks (VLANs) are often used in an enterprise network?

虛擬區網 (VLAN, Virtual Local Area Network) 為一種高階交換器 (high-end switch) 功能,其特色包含如下:

  1. 可依須求,將交換器某連接埠 (port),綁定到某網段 (VLAN ID),凡連線此連接埠之設備流量,將歸屬該網段。也可將某網卡 MAC 住址,綁定到某網段 (VLAN ID),凡該網卡進來之流量,將歸屬該網段。
  2. 只有歸屬同一個 VLAN ID 的終端設備,才能收到彼此廣播 (broadcast) 及單播 (unicat) 訊息。
  3. 若依部門分配 VLAN ID,有助於部門內安全互通,不受其他部門干擾。
  4. 交換器連接埠分成存取埠 (access port) 及骨幹埠 (trunk port)。存取埠 負責連到終端設備,須設定屬於哪個網段 (VLAN ID)。骨幹埠 負責連到其他交換器 (switch) 或路由器 (router),會在傳送接收訊框 (frame) 時,利用額外添加的特定標籤 (tag) ,區別訊框所屬 VLAN ID,故可同時傳送接收各虛擬區網訊息。

另外,虛擬區網 遇不同 VLAN 網段 須要交換資訊時,必須透過 VLAN 網段的對外閘道器 (gateway) 繞送封包。為節省介面成本,常將 路由器區網介面 切割成很多 子介面 (subinterface),每個子介面對應一個 VLAN 網段的對外閘道器。

動態骨幹協定 (DTP, Dynamic Trunking Protocol) 為思科專屬協定,在思科 Catalyst 2960 和 Catalyst 3650 觸媒系列交換器上會自動啟用,依連線雙方的組態匹配,加速連接埠決定要當成 存取埠 或 骨幹埠 使用。

又企業常使用第 3 層交換器 (Layer 3 Switch),其支援硬體式交換,可達到比路由器更高的封包處理速率。在第3層交換器上,設定 VLAN 路由時,不須切割子介面,只要在各虛擬區網介面 (vlan interface) 設定其閘道IP住址,再啟動路由繞送 (ip routing) 功能即可。

2021年12月11日 星期六

why use high-end switches in an enterprise LAN environment?

企業 (enterprise) 若在區網 (LAN, Local Area Network) 使用第1層 (OSI Model Layer 1) 集線器 (Hub, Repeater, Concentrator) 設備互連,其缺點是整個集線器連線的設備同屬一個踫撞區域 (Collision Domain),任一組網卡佔住頻寬講話,其他網卡就只能旁聽等候,集線器一次只允許一組網卡對談

企業若在區網使用第2層 (OSI Model Layer 2) 交換器 (Switch, Bridge) 設備互連的好處是,由於交換器會學習 MAC住址表 (Media Access Control Address Table), 記錄每個 MAC 住址曾出現於哪個交換器連接埠出口,因此,若去處 MAC 住址所在的出口不衝突,交換器一次允許多組網卡同時間對談

如上所述,針對1對1單播訊框 (Unicast Frame),企業若在區網內使用交換器,於收到訊框 (frame) 時,依照其去處 MAC 住址 (Destination MAC Address) 欄位值,決定訊框轉送的連接埠出口,將可提升同時間會談量,達成增加頻寬效果。這效果乍看不錯,但其實是所有第2層交換器設備的基本功能。至於較高階的交換器 (high-end switch),其實還支援延伸樹協定 (STP, Spanning Tree Protocol),及虛擬區網 (VLAN, Virtual Local Area Network) 功能。上述這3種功能有一共通特色,就是皆可以為區網流量 (traffic) 作適當分隔,讓流量儘量只往該去的路線送,不讓不該看的人看到。

    簡言之,企業在區網內會使用較高階的交換器,可歸結原因為其看重如下3種分隔區網流量功能:
  1. 支援 MAC 住址表,可依照去處 MAC 住址,決定訊框轉送出口,提升同時間會談數
  2. 支援延伸樹 STP 協定,遇多部交換器串接時,可決定各連接埠暫時開啟或關閉傳送功能,不會出現迴路造成廣播風暴
  3. 支援虛擬區網 VLAN 功能,可任意跨交換器轉送訊框給相同 VLAN 接收者,不擔心資料外洩給其他 VLAN 用戶

2021年8月27日 星期五

how to query OSM data in Java?

開放街圖OSM的公共圖資可以透過overpass-turbo介面查詢獲得。
若想利用Java程式碼查詢,可參考以下查詢範例。
其中,查詢指令可參考overpass-turbo範例指令,測試成功再代入程式。


/*
  QueryOSM.java
        展示如何用retrofit套件,同步及非同步(適用於Android),存取如下OSM圖資服務
                http://overpass-api.de/api/interpreter?data=xxx

        // 建立服務連線客戶端及請求內容
        OverpassService requestClient = OverpassServiceProvider.get();
        String request = composeRequest();
        
        // 非同步查詢圖資,適用於手機平板Android平台
        asyncRequest(requestClient, request);
        
        // 同步查詢圖資,適用於桌機Application應用
        OverpassQueryResult result = syncRequest(requestClient, request);
        postProcess(result);
 
執行步驟:
  javac QueryOSM.java
  java QueryOSM
run:
[out:json][timeout:25];
(
node[tourism](25.1735, 121.446, 25.1775, 121.455);
relation[!highway][type!='route'][!boundary](25.1735, 121.446, 25.1775, 121.455);
);
out center;
end of asyncRequest()
21 elements...
1. id:4326372453, type:node, lat:25.1761808, lon:121.4490491, name:五虎碑, wheelchair:limited
2. id:4384988658, type:node, lat:25.1750071, lon:121.4522078, name:文錙藝術中心, wheelchair:yes
3. id:4492221396, type:node, lat:25.1736267, lon:121.4473809, name:三化牆
4. id:4492221397, type:node, lat:25.1744069, lon:121.4474037, name:地球村雕像, wheelchair:limited
5. id:4492221399, type:node, lat:25.1735175, lon:121.4484658, name:淡江大學花牆
6. id:4492221425, type:node, lat:25.1749949, lon:121.450678, name:閱讀的少女, wheelchair:yes
7. id:4502075211, type:node, lat:25.1751384, lon:121.4523232, name:旅者
8. id:4502075212, type:node, lat:25.1739938, lon:121.4505047, name:李雙澤紀念碑, wheelchair:yes
9. id:4502075213, type:node, lat:25.1761919, lon:121.4499374, name:福園金鷹銅雕, wheelchair:no
10. id:4502075222, type:node, lat:25.1738907, lon:121.4475716, name:驚聲銅像, wheelchair:limited
11. id:4507662408, type:node, lat:25.1741082, lon:121.4474671, name:溫馨, wheelchair:yes
12. id:5012978611, type:node, lat:25.1741784, lon:121.4507282, name:黃河母親, wheelchair:no
13. id:5072580167, type:node, lat:25.1769149, lon:121.4495309
14. id:5130535622, type:node, lat:25.1757202, lon:121.4496844, name:會文館, wheelchair:yes
15. id:5132288341, type:node, lat:25.1741586, lon:121.4508061
16. id:5132288342, type:node, lat:25.174208, lon:121.4475417
17. id:6050843218, type:node, lat:25.1770813, lon:121.449821
18. id:8991981256, type:node, lat:25.1750049, lon:121.4480033, name:淡江願景牆, wheelchair:yes
19. id:3974590, type:relation, lat:0.0, lon:0.0, type:multipolygon, name:操場
20. id:3983402, type:relation, lat:0.0, lon:0.0, type:multipolygon, name:松濤廣場
21. id:7530081, type:relation, lat:0.0, lon:0.0, type:multipolygon

Response{protocol=http/1.1, code=200, message=OK, url=http://overpass-api.de/api/interpreter?data=%5Bout%3Ajson%5D%5Btimeout%3A25%5D%3B%0A%28%0Anode%5Btourism%5D%2825.1735%2C%20121.446%2C%2025.1775%2C%20121.455%29%3B%0Arelation%5B%21highway%5D%5Btype%21%3D%27route%27%5D%5B%21boundary%5D%2825.1735%2C%20121.446%2C%2025.1775%2C%20121.455%29%3B%0A%29%3B%0Aout%20center%3B}
end of syncRequest()
21 elements...
1. id:4326372453, type:node, lat:25.1761808, lon:121.4490491, name:五虎碑, wheelchair:limited
2. id:4384988658, type:node, lat:25.1750071, lon:121.4522078, name:文錙藝術中心, wheelchair:yes
3. id:4492221396, type:node, lat:25.1736267, lon:121.4473809, name:三化牆
4. id:4492221397, type:node, lat:25.1744069, lon:121.4474037, name:地球村雕像, wheelchair:limited
5. id:4492221399, type:node, lat:25.1735175, lon:121.4484658, name:淡江大學花牆
6. id:4492221425, type:node, lat:25.1749949, lon:121.450678, name:閱讀的少女, wheelchair:yes
7. id:4502075211, type:node, lat:25.1751384, lon:121.4523232, name:旅者
8. id:4502075212, type:node, lat:25.1739938, lon:121.4505047, name:李雙澤紀念碑, wheelchair:yes
9. id:4502075213, type:node, lat:25.1761919, lon:121.4499374, name:福園金鷹銅雕, wheelchair:no
10. id:4502075222, type:node, lat:25.1738907, lon:121.4475716, name:驚聲銅像, wheelchair:limited
11. id:4507662408, type:node, lat:25.1741082, lon:121.4474671, name:溫馨, wheelchair:yes
12. id:5012978611, type:node, lat:25.1741784, lon:121.4507282, name:黃河母親, wheelchair:no
13. id:5072580167, type:node, lat:25.1769149, lon:121.4495309
14. id:5130535622, type:node, lat:25.1757202, lon:121.4496844, name:會文館, wheelchair:yes
15. id:5132288341, type:node, lat:25.1741586, lon:121.4508061
16. id:5132288342, type:node, lat:25.174208, lon:121.4475417
17. id:6050843218, type:node, lat:25.1770813, lon:121.449821
18. id:8991981256, type:node, lat:25.1750049, lon:121.4480033, name:淡江願景牆, wheelchair:yes
19. id:3974590, type:relation, lat:0.0, lon:0.0, type:multipolygon, name:操場
20. id:3983402, type:relation, lat:0.0, lon:0.0, type:multipolygon, name:松濤廣場
21. id:7530081, type:relation, lat:0.0, lon:0.0, type:multipolygon

end of main()
BUILD SUCCESSFUL (total time: 1 minute 2 seconds)

  參考:
  a. Retrofit 2 – Synchronous and asynchronous call example
        https://howtodoinjava.com/retrofit2/retrofit-sync-async-calls/
  b. https://github.com/zsoltk/overpasser
        hu.supercluster.overpasser.adapter
            OverpassQueryResult
            OverpassQueryResult.Element
            OverpassQueryResult.Element.Tags
            OverpassService
  c. 引用函數庫
      compile/run: 
        okhttp-3.14.9.jar
        okio-1.17.2.jar
        converter-gson-2.10.2.jar
        gson-2.8.5.jar
        retrofit-2.10.2.jar
      compile/run tests:
        byte-buddy-1.11.3.jar
        byte-buddy-agent-1.11.3.jar
        mockito-core-3.11.2.jar
        objenesis-3.2.jar
 */
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

import hu.supercluster.overpasser.library.output.OutputFormat;
import hu.supercluster.overpasser.library.output.OutputModificator;
import hu.supercluster.overpasser.library.output.OutputOrder;
import hu.supercluster.overpasser.library.output.OutputVerbosity;
import hu.supercluster.overpasser.library.query.OverpassQuery;
 
import hu.supercluster.overpasser.adapter.OverpassQueryResult;
import hu.supercluster.overpasser.adapter.OverpassQueryResult.Element;
import hu.supercluster.overpasser.adapter.OverpassQueryResult.Element.Tags;
import hu.supercluster.overpasser.adapter.OverpassService;
import hu.supercluster.overpasser.adapter.OverpassServiceProvider;

import java.lang.reflect.Field;
import java.util.List;
/**
 *
 * @author seke
 */

public class QueryOSM {
    
    // 可利用下址測試查詢指令
    //     http://overpass-turbo.eu/
    public static String composeRequest()
    {
/* 查詢範例1: 
        在(47.48047027491862,19.039797484874725,47.51331674014172,19.07404761761427)範圍內
        列出所有非私人停車場
        
    A. 查詢指令
    ["out":"json"]["timeout":"30"];
    (
        node
            ["amenity"="parking"]
            ["access"!="private"]
            (47.48047027491862,19.039797484874725,47.51331674014172,19.07404761761427);
            <;
    );
    out body center qt 100;

    B. 組合查詢指令方法
      String query = new OverpassQuery()
        .format(OutputFormat.JSON)
        .timeout(30)
        .filterQuery()
            .node()
            .amenity("parking")
            .tagNot("access", "private")
            .boundingBox(
                47.48047027491862, 19.039797484874725,
                47.51331674014172, 19.07404761761427
            )
        .end()
        .output(OutputVerbosity.BODY, OutputModificator.CENTER, OutputOrder.QT, 100)
        .build()
        ;
*/

/* 查詢範例2: 
        在(25.1735, 121.446, 25.1775, 121.455)範圍內,撈取
           非座椅設施,商店,觀光點,辦公室,繄急設施之節點
           屬於大學設施之線條,關係
           不屬於公路、路線、森林、邊界之線條,關係
        列出其中心位置及相關屬性(標籤)

        A. 查詢指令
    [out:json][timeout:25];
    // gather results
    (
      node[amenity][amenity!=bench](25.1735, 121.446, 25.1775, 121.455);
      node[shop](25.1735, 121.446, 25.1775, 121.455);
      node[tourism](25.1735, 121.446, 25.1775, 121.455);
      node[office](25.1735, 121.446, 25.1775, 121.455);
      node[emergency](25.1735, 121.446, 25.1775, 121.455);
      //way[amenity="university"](25.1735, 121.446, 25.1775, 121.455);
      relation[amenity="university"](25.1735, 121.446, 25.1775, 121.455);
      way[!highway][type!="route"][landuse!="forest"][!boundary](25.1735, 121.446, 25.1775, 121.455);
      relation[!highway][type!="route"][!boundary](25.1735, 121.446, 25.1775, 121.455);
    );
    // print results
    out center;
---
  註:  淡江大學之範圍為 25.1735, 121.446, 25.1775, 121.455
*/       
       String query = String.join("\n",
               "[out:json][timeout:25];",
               "(",
//               "node[amenity][amenity!=bench](25.1735, 121.446, 25.1775, 121.455);",
//               "node[shop](25.1735, 121.446, 25.1775, 121.455);",
               "node[tourism](25.1735, 121.446, 25.1775, 121.455);",
//               "node[office](25.1735, 121.446, 25.1775, 121.455);",
//               "node[emergency](25.1735, 121.446, 25.1775, 121.455);",
//               "relation[amenity='university'](25.1735, 121.446, 25.1775, 121.455);",
//               "way[!highway][type!='route'][landuse!='forest'][!boundary](25.1735, 121.446, 25.1775, 121.455);",
               "relation[!highway][type!='route'][!boundary](25.1735, 121.446, 25.1775, 121.455);",
               ");",
               "out center;");

        System.out.println(query);
        return query;
    }
    
    public static void asyncRequest(OverpassService apiClient, String request)
    {
       //Call call = service.interpreter(query);
       apiClient.interpreter(request).enqueue(new Callback()
       {
        @Override
        public void onResponse(Call call, Response response)
        {
            if(response.isSuccessful()==false)
            {
                System.out.println("asyncRequest: response.isSuccessful(): false");
                System.out.println(response.errorBody());
                return;
            }
            
            OverpassQueryResult result = response.body();
            postProcess(result);
         }

        public void onFailure(Call call, Throwable t) {
        // DO failure handling 
          System.out.println("onFailure");
          System.out.println(t.getLocalizedMessage());
        }
       });
       
       System.out.println("end of asyncRequest()");
    }
    
    public static OverpassQueryResult syncRequest(OverpassService apiClient, String request)
    {
        OverpassQueryResult result = null;
        Call callSync =   apiClient.interpreter(request); 

        try
        {
            Response response = callSync.execute();
            //OverpassQueryResult apiResponse = response.body();
     
            //API response
            System.out.println(response);
            result = response.body();
        }
        catch (Exception ex) 
        { 
            ex.printStackTrace();
        }
        
        System.out.println("end of syncRequest()");
        return result;
    }
    
    public static void postProcess(OverpassQueryResult result)
    {
        if(result==null) return;

        // DO success handling 
        StringBuilder sb = new StringBuilder();
        System.out.println(result.elements.size() + " elements...");
        int count = 1;
        for (Element p : result.elements) 
        {
            sb.append(count); count++;
            sb.append(String.format(". id:%s, type:%s, lat:%s, lon:%s", p.id, p.type, p.lat, p.lon));
            Tags tags = p.tags;
            for (Field f : tags.getClass().getFields()) {
                f.setAccessible(true);
                try 
                {
                    if (f.get(tags) != null) {
                       sb.append(String.format(", %s:%s", f.getName(), f.get(tags)));
                    }
                }
                catch (IllegalAccessException e)
                { // shouldn't happen because I used setAccessible
                }

            }
            //if(p.tags. != null)
            //  sb.append(String.format("tags:%s", p.tags.name));
            sb.append("\n");
        }
        System.out.println(sb.toString());
    }
    
    public static void main(String args[])
    {
        // 建立服務連線客戶端及請求內容
        OverpassService requestClient = OverpassServiceProvider.get();
        String request = composeRequest();
        
        // 非同步請求
        asyncRequest(requestClient, request);
        
        // 同步請求
        OverpassQueryResult result = syncRequest(requestClient, request);
        postProcess(result);
        
        System.out.println("end of main()");
    }
}

2021年8月17日 星期二

tips on installation of xrdp for remote desktop connection to linux machines

大家都知道,Linux桌面跑的是X Windows視窗系統。微軟Windows如果想要連上Linux桌面,一種作法是本身主機消耗硬碟,裝上X Windows伺服軟體VcXsrv,甚至加上微軟應用商店的免費Linux作業系統,例如Ubuntu,此作法步驟可詳Lainme's Blog。另一種作法是遠方Linux主機安裝tightvnc server及xrdp套件,微軟Windows只要沿用原來的遠端桌面連線(mstsc.exe),即可使用rdp協定,連上Linux桌面。後者作法,剪貼簿可用,聲音則待個別測試。以下將依據Linux作業系統為 Ubuntu 或 CentOS,分別介紹其安裝訣竅。

A. Ubuntu 安裝 xrdp 作法

-- 先去除舊套件干擾 sudo apt-get remove xrdp tightvncserver # 只刪執行檔 sudo apt-get purge xrdp tightvncserver # 刪除執行檔及相關組態檔 sudo dpkg --purge xrdp tightvncserver # 刪除執行檔及相關組態檔 sudo dpkg -S /etc/xrdp/startwm.sh # 查檔案所屬套件 -- 安裝tightvncserver及xrdp套件 sudo apt-get install tightvncserver # 一定要先安裝此視窗套件 sudo apt-get install xrdp # 然後安裝此傳輸套件,才會順利偵測運用視窗套件 sudo vi /etc/xrdp/startwm.sh # 加入管理視窗指令 xfce4-session # 若未安裝,可使用apt-get install xfce4-session -- 啟動服務 sudo service xrdp status # 詢問xrdp服務狀態 sudo service xrdp start # 啟動xrdp服務 註: 若連線過程,顯示詢息,缺rsakeys.ini金鑰,可如下產生金鑰 cd /etc/xrdp sudo xrdp-keygen xrdp # generate /etc/xrdp/rsakeys.ini -- 測試連線 sudo netstat -tulpn # 觀看是否3389及3350連接埠有監聽服務 -- rdp連線過程,適用於 Ubuntu 16.04.6 LTS (1) xrdp (3389) # 選擇 sesman-Xvnc 模式 (2) xrdp-sesman (3350,root) (3) xrdp-chansrv # 執行 /etc/xrdp/startwm.sh 及 xfce4-session (4) Xvnc (59xx) -- rdp連線過程,適用於 Ubuntu 18.04.5 LTS (1) xrdp (3389) # 選擇 Xorg 模式 (2) xrdp-sesman (3350,root) (3) xrdp-chansrv # 執行 /etc/xrdp/startwm.sh 及 xfce4-session (4) Xorg 註: xrdp安裝apt-get內建版本即可,不須安裝 https://github.com/neutrinolabs/xrdp.git

B. CentOS 安裝 xrdp 作法

-- 安裝tightvnc-server及xrdp套件 sudo yum erase xrdp tightvnc tightvnc-server sudo yum install tightvnc-server # 要先裝tightvnc-server sudo yum install xrdp # 再裝xrdp -- sudo repoquery -l xrdp # 查套件安裝哪些檔案 sudo rpm -ql /usr/bin/Xvnc # 查檔案所屬套件 -- cat /etc/xrdp/startwm.sh # 確認系統裝有如下任一種視窗管理程式 SESSIONS="gnome-session blackbox fluxbox startxfce4 startkde xterm" -- 啟動服務 sudo service xrdp status # 詢問xrdp服務狀態 sudo service xrdp start # 啟動xrdp服務 -- 測試連線 sudo netstat -antup | grep xrdp # 觀看是否3389及3350連接埠有監聽服務 註: 若連線過程,顯示訊息,缺權限,可如下給予selinux權限 sudo chcon --type=bin_t /usr/sbin/xrdp sudo chcon --type=bin_t /usr/sbin/xrdp-sesman -- rdp連線過程,適用於 CentOS 6.10 (1) xrdp (3389) # 選擇 sesman-Xvnc 模式 (2) xrdp-sesman (3350,root) (3) xrdp-chansrv # 執行 /etc/xrdp/startwm.sh 及 gnome-session (4) Xvnc (59xx) 註: 若為舊 CentOS-6,因自2020/11/30已不再提供維護更新,故須更新repo來源,如下: sudo rm CentOS-SCLo-scl* sudo curl https://www.getpagespeed.com/files/centos6-eol.repo --output /etc/yum.repos.d/CentOS-Base.repo sudo curl https://www.getpagespeed.com/files/centos6-epel-eol.repo --output /etc/yum.repos.d/epel.repocurl \ https://www.getpagespeed.com/files/centos6-epel-eol.repo --output /etc/yum.repos.d/epel.repo sudo yum update # 更新到6.10版 參考: 阿吉的部落格: 使用 xrdp 遠端登入 Linux (Gentoo)

2021年5月28日 星期五

how to clone the conda environment from a path?

執行 Python 語言由於程式的套件相依性很強,稍微不同版本套件可能程式就跑不起來。
因此,常須鎖定安裝特定套件版本,建置成一個程式專用執行環境。

常見的 Anaconda 軟體提供 Python環境管理功能,在Ubuntu等Linux作業系統上,
其管理程式 conda 一般安裝路徑為 /usr/local/anaconda3/bin/conda。

Linux作業系統上,用戶foobar的conda環境安裝路徑一般位於其家目錄,如下:

   /home/foobar/.conda/envs/

此時,利用 conda env list 指令,可列出家目錄下有多少環境。

假設用戶因空間問題,由管理者已將家目錄設定成新路徑 /home2/foobar/。
這時,為免重建執行環境的麻煩,可用如下指令為環境myenv拷貝一份到新家目錄。

   conda create --clone /home/foobar/.conda/envs/myenv --name myenv

其效果等同於將舊家目錄環境

   /home/foobar/.conda/envs/myenv/

拷貝一份到新家目錄

   /home3/foobar/.conda/envs/myenv/
   
拷貝後,同樣可利用 conda env list 指令,列出新家目錄下是否有 myenv 環境。

2021年3月30日 星期二

chlorine gas uses sigma p or sigma sp3 bonding?

ⅦA氟族氯氣(Cl₂)分子由兩顆氯原子(Chlorine)以單鍵,即σ鍵結合而成。每顆氯原子其原子序17,電子組態[Ne] 3s²3p⁵,最外殼層有7個價電子,其中6個成對,1個未成對。根據八隅體規則(Octet Rule),將兩個氯原子的未成對電子重疊共用,讓每個氯原子周圍有8個價電子時,氯氣分子可達到最大穩定度。對於氯氣的σ鍵組成是σp-p或σsp3-sp3混成軌域(hybridized orbitals),目前兩種說法都有支持者如下。

  1. σp-p: 依據兩個p軌域未成對電子重疊共用,可形成八隅體穩定現象,而支持氯氣的σ鍵組成是σp-p的說法有
    Chemistry - Molecular Structure (27 of 45) Sigma(p-p) Bond - Example
  2. σsp3-sp3: 依據價殼層電子對互斥理論(VSEPR, Valence Shell Electron Pair Repulsion Theory),氯原子周圍空間位數(Steric Number)=鍵結數1+孤電子對數3=4,而支持氯氣的σ鍵組成是σsp3-sp3的說法有
    Hybridization of Cl2 (description of hybrid orbitals for Chlorine)

由於兩者預測的分子形狀都是直線形,故無法由預測形狀區別,只能期待由實驗獲得的鍵長或鍵強度判定。相關的ⅦA氟族、ⅥA氧族、或VA氮族氣體分子應該也有類似的σ鍵組成疑問待證實。

2021年1月6日 星期三

how to manage self-select user groups on moodle

使用Moodle教學平台,如果想讓同學自由分組,有兩種作法。第一種是使用外加的Group self-selection module模組,操作較簡便,但此非預設模組,須請Moodle管理者安裝才能用此分組活動。第二種是從預設模組手動管理,操作較麻煩,但可利用現有票選(Choice)活動達成,不失為可行的替代方案。以下將介紹第二種手動管理法

1. 開啟【票選(Choice)】活動C,設定截止期限之前,允許學生自由變更組別。
   假設想分成 N=10 組,每組人數上限 M=5 人,則可規劃
     team00, team01, team02, ..., team10, team11, team12, team13, team14, team15共16組。
   其中,team00為教師,助教群組,供了解學生操作畫面。team01 ~ team10為實際10個分組。
       team11 ~ team15為備用分組,供臨時拆分組暫存之用。
   活動的選項設定如下:
     是否允許變更選擇: 
     允許選擇一個以上的選項: 
     限制回答的次數: 
      選項1: team00, 限制1: 2
      選項2: team01, 限制2: 6
      .....
      選項16: team15, 限制16: 6
   其中,限制人數上限為M+1=6,供緩衝之用。最終仍可要求每組M=5人。

2. 匯出 票選活動的分組結果 成為 外部分組名單檔案
   進入【票選(Choice)】活動C,點選【查看?個回應/以Excel檔案格式下載】。
   打開C.xlsx檔案,可看到同學自由分組結果,如下。
      學號    名字    學號   分組      選擇 {$a}
      rrrrr   nnnn   rrrr   team07    team07
      .....
   其中,【分組】欄為學生目前所屬組別,【選擇{$a}】欄為學生選取組別。
   兩者相同表示學生沒打算變動組別,兩者不同表示學生想變動組別。
   留下【學號】【選擇 {$a}】兩欄位,整理成如下分組匯入格式,存成外部C.csv檔
      username, group
      學號1, team01
      學號2, team01
      .....
      學號n, team02

3. 批次匯入 外部分組名單檔案 成為 課程分組名單
   點選【系統管理/課程管理/用戶/匯入學生名單:】
    檔案位置:選擇一檔案,C.csv
    CSV分隔符號: ,
    編碼: UTF-8
    Role to assign: 學生
    First column contains: Id number
    Create group(s) if needed: 
    Create grouping(s) if needed:  ** (新分組是否獨立為新分群)
    Send me a mail report: 
    點選【加入課程】
    ---
    username xxx already enroled and added to Moodle's group team yy
    .....
    0 enroled
    zz group(s) created : team01, team02, ...
    tt grouping(s) created :

4. 產生 分組摘要表 供確認
   點選【系統管理/課程管理/用戶/分組/概要】,將摘要表拷貝到專屬頁面供學生確認。

5. 若想局部修正 分組名單,可修改C.csv檔,依步驟3.重新匯入。
   或點選【系統管理/課程管理/用戶】,由分組/成員/新增移除使用者,增減分組成員。

6. 取得最後課程設定好的 分組名單
   可重複步驟2,再次利用【票選(Choice)】活動C,匯出名單C.xlsx。
   留下其中的【學號】【分組】兩欄位即可。

以上步驟3,用檔案批次匯入名單的角色非常關鍵,可免去步驟5,點選逐一加入名單的辛勞。

參考: 
1. [Moodle 2.6-2.8] How to download the list of participants with groups in a course
2. Blog: moodle administration memo