雙匯發展多個分廠的能源管控大數據系統主要采用兩種技術棧:InfluxDB/Redis和Kafka/Redis/HBase/Flink,對於中小型研發團隊來講,無論是系統搭建,還是實施運維都非常棘手。經過對InfluxDB/Redis和TDengine大數據平台的功能和性能對比測試,最終將TDengine作為實施方案。
1. 項目背景
基於雙匯發展對能源管控的需求,利用雲平台技術以及電氣自動化處理手段,對雙匯發展的一級、二級、三級能源儀表進行整體改造,實現儀表組網,進一步通過邊緣網關進行能源在線監測數據的采集,並上報至雲平台,建立統一能源管理信息化系統,實現能源的實時監控、報表統計、能源流向分析與預測,降低企業單位產品能源消耗,提高經濟效益,最終實現企業能源精細化管理。
2. 總體架構
能源管控平台基於私有雲構建,包括完整的IaaS層、PaaS層和SaaS層,而能源采集系統作為管控平台中最為重要的一環,采用TDengine作為核心數據引擎,通過Restful接口進行儀表在線數據插入,並實現大規模時序數據的高效穩定存儲,同時,也為能源管控應用層提供實時數據查詢、歷史聚合統計、流計算和訂閱服務等功能,實現能源地圖監控、能耗預警、能源流向預測和能源互聯綜合決策,具體架構如下圖所示。
圖1 能源采集系統架構
3. TDengine關鍵應用
3.1 Connector選擇
本項目數據采集最關鍵的環節,就是將訂閱到的MQTT數據插入到TDengine中,於是也就涉及到了TDengine連接器的選擇,我們平時項目中java使用居多,而且JDBC的性能也相對較強,理論上,應該選擇JDBC API,但最終選擇了RESTful Connector,主要考慮以下幾點:
1)簡潔性
毫無疑問,RESTful通用性最強,TDengine直接通過HTTP POST 請求BODY中包含的SQL語句來操作數據庫,而且TDengine本身作為時序數據庫並不提供存儲過程或者事務機制,基本上都是每次執行單條SQL語句,所以RESTful在使用上很簡便。
2)可移植性
本項目的Java應用都是部署在Kubernetes中,所以向TDengine插入數據的Java應用需要容器化部署,而之前了解到,JDBC需要依賴的本地函數庫libtaos.so文件,所以容器化部署可能較為麻煩,而RESTful僅需采用OKHttp庫即可實現,移植性較強。
3)數據規模
本項目數采規模不大,大約每分鍾7000條數據,甚至后續數采功能擴展到其他分廠,RESTful也完全滿足性能要求。
但總體來講,JDBC是在插入與查詢性能上具有一定優勢的,而且支持從firstEp和secondEp選擇有效節點進行連接(類似於Nginx的keepalive高可用),目前TDengine版本發布情況上看,JDBC的維護與提升也是重中之重,后續項目也可能會向JDBC遷移。
3.2 RESTful代碼實現
1)ThreadPoolExecutor線程池
訂閱EMQX和RESTful插入TDengine的代碼寫在了同一個java服務中,每接收到一條MQTT訂閱消息,便開啟一個線程向TDengine插入數據,線程均來自於線程池,初始化如下:
ExecutorService pool =newThreadPoolExecutor(150,300,1000,TimeUnit.MILLISECONDS,newArrayBlockingQueue<Runnable>(100),Executors.defaultThreadFactory(),newThreadPoolExecutor.DiscardOldestPolicy());
線程池采用基於數組的先進先出隊列,采用丟棄早期任務的拒絕策略,因為本次場景中單次RESTful插入數據量大約在100~200條,執行速度快,遲遲未執行完極可能是SQL語句異常或連接taosd服務異常等原因,應該丟棄任務。核心線程數設為150,相對較高,主要為了保證高峰抗壓能力。
2)OKHttp線程池
在每個ThreadPoolExecutor線程中,基於OKHttp庫進行RESTful插入操作,也是采用ConnectionPool管理 HTTP 和 HTTP/2 連接的重用,以減少網絡延遲,OKHttp重點配置如下:
publicConnectionPool pool(){returnnewConnectionPool(20,5,TimeUnit.MINUTES);}
即最大空閑連接數為20,每個連接最大空閑時間為5分鍾,每個OKHttp插入操作采用異步調用方式,主要代碼如下:
publicvoid excuteTdengineWithSqlAsync(String sql,Callback callback){
try{
okhttp3.MediaType mediaType = okhttp3.MediaType.parse("application/octet-stream");
RequestBody body =RequestBody.create(mediaType, sql);
Request request =newRequest.Builder()
.url(tdengineHost)
.post(body)
.addHeader("Authorization","Basic cm9vdDp0YW9zZGF0YQ==")
.addHeader("cache-control","no-cache")
.build();
mOkHttpClient.newCall(request).enqueue(callback);
}catch(Exception e){
logger.error("執行tdengine操作報錯:"+ e.getMessage());
}
}
3)Java打包鏡像
長期壓力測試顯示,每秒執行200次RESTful插入請求,單次請求包含100條數據,每條數據包含5組標簽,Java服務內存穩定在300M~600M。而且上述模擬規模僅針對單個Java應用而言,在Kubernetes可以跑多個這樣pod來消費不同的MQTT主題,所以並發能力完全夠用。打包鏡像時,堆內存最大值設為1024MB,主要語句為:
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-XX:MaxRAM=2000m","-Xms1024m","-jar","/app.jar"]
3.3 性能測試
1)RESTful插入性能
按照3.2小節中的RESTful代碼進行數據插入,Java程序和TDengine集群均運行在私有雲中,虛擬機之間配備萬兆光纖交換機,Java程序具體如3.2小節所示,TDengine集群部署在3個虛擬機中,配置均為1TB硬盤、12核、12GB(私有雲中CPU比較充裕,但內存比較緊張),經過大約三周的生產環境運行,性能總結如下:
表1 生產環境下RESTful插入性能測試
生產環境下,單條插入性能極高,完全滿足需求,當然前期也進行過稍大規模的插入場景模擬,主要是基於2.0.4.0以后的版本,注意到2.0.4.0之前的TDengine版本RESTful的SQL語句上限為64KB。模擬環境下,RESTful插入性能非常優秀,具體如下表所示。
表2 模擬環境下RESTful插入性能測試
2)RESTful查詢性能
使用RESTful進行SQL查詢時,性能也是非常好,目前真實生產環境中,數據總量為800萬,相對單薄,所以查詢性能測試在模擬環境下進行,在8億數據量下,LAST_ROW函數可以達到10ms響應速度,count、interval、group by等相關函數執行速度均在百毫秒量級上。
3.4 實施方案
本項目針對雙匯發展下屬的6個分廠(后續會繼續擴充)進行能源數據采集,大約1200多塊儀表(后續會繼續擴充),每塊儀表包括3至5個采集標簽,采集頻率均為1分鍾,數據接入規模不大。六個廠各自有獨立的租戶空間,為了方便各自的時序數據庫管理,同時也方便各廠間的聚合查詢(目前六個分廠均從屬雙匯發展總部),所以各分廠分別建立超級表,每個超級表包括4個tag,分別為廠編號、儀表級別、所屬工序和儀表編號,具體超級表建表情況如下圖所示。
主要用到的集群包括TDengine集群、EMQX集群和Redis集群,其中Redis集群在數據采集方面,僅僅用於緩存儀表連接狀態,其重點在於緩存業務系統數據;EMQX集群用於支撐MQTT數據的發布與訂閱,部署在Kubernetes中,可以實現資源靈活擴展;TDengine集群部署在IaaS虛擬機中,支持大規模時序數據的存儲與查詢。
表3 集群配置信息
按照TDengine官方的建議,“一個數據采集點一張表,同一類型數據采集點一張超級表”,我針對不同分廠的水表、電表、蒸汽表和燃氣表分別建立的超級表,每個儀表單獨建表,保證每張表的時間戳嚴格遞增。在實踐TDengine的過程中,重點體會如下:
1)集群搭建門檻低
TDengine集群安裝部署非常便捷,尤其相比於其他集群,僅需要簡單的配置就可以實現生產環境級的搭建,官方文檔也比較豐富,社區活躍,也大為降低了后續運維成本。
2)插入與查詢效率極高
TDengine的插入與查詢性能極高,這點在實際運行時也深有感觸,用last_row函數查詢儀表最新數據,基本上可以達到毫秒級,在幾十億級的數據上進行聚合查詢操作,也可達到百毫秒級,極大提供了系統的響應速度。
3)全棧式時序處理引擎
在未使用TDengine之前,我們主要采用InfluxDB/Redis和Kafka/Redis/HBase/Flink兩種技術棧,對於我們中小型研發團隊來講,無論是系統搭建,還是實施運維都非常棘手。但是使用TDengine后,一切都簡化了,TDengine將數據庫、消息隊列、緩存、流式計算等功能融合一起,以一種全棧的方式,為我們的大數據系統帶來了便捷。技術方案的對比如表4所示。
注:方案一為InfluxDB/Redis,方案二為Kafka/Redis/HBase/Flink,方案三為TDengine
表4 數據采集方案對比
從表4的對比方案中可以看出,TDengine(方案三)是有着很大的優勢,尤其在開源EMQX Broker的支持上也非常好(主要依賴於Restful接口),其他的例如Kafka和InfluxDB只能和企業版EMQX集成;在數據插入和查詢效率方面,上述三種方案關鍵在於TDengine、HBase和InfluxDB的對比,官網有非常詳細的測試報告,TDengine也是有絕對優勢,這里就不過多敘述。所以選擇TDengine是勢在必行的。
3.5 技術期望
在時序數據庫性能方面,TDengine有着很大的優勢,並且也集成了消息訂閱和流計算功能,可以說在中小型物聯場景下,是無需部署Kafka和Flink的。當然個人理解TDengine不是為了完全取代Kafka和Flink而生的,尤其是在大型雲服務項目中,更多是共存。
但是在邊緣端,TDengine憑借着極低的資源占用率和優秀的時序處理性能,將會產生更大的能量,期望能徹底集成邊緣流計算和MQTT broker等功能,擴充Modbus、OPC-UA等常見工業協議支持,向下連接工業設備或者物聯設施,向上和邊緣Kubernetes生態(如KubeEdge、K3S等)協同,或者直接和雲中心協同。
3.6 系統運行界面
項目重點是能耗統計,而在線采集到TDengine里的數據都是累計量,所以在計算能耗時,需要在不同的超級表執行按表分組、按時間周期采樣的查詢,類似下面語法:
selectlast(累計列)as max_val,first(累計列)as min_val from[超級表名]where[標簽欄相關過濾]and ts>=now-10h INTERVAL(1h)groupby[儀表編號];
得益於TDengine的極佳性能,基本能保證不超過百毫秒的訪問延時,下面是一些相關的PC端、移動端界面(我們移動端是用H5做的,為了直接能跑在Android和iOS上)。
寫在最后
其實從2019年開始就一直在關注TDengine,也看了很多陶總的演講,受益匪淺,尤其在今年8月份,TDengine進行了集群版開源,也正好准備啟動能源數據采集項目,所以果斷采用TDengine作為核心時序引擎,目前也是收獲了非常的效果。本次項目實施過程中,尤其感謝濤思數據的蘇曉慰工程師,多次協助解決TDengine相關的實施問題。計划在后續其他項目也也會繼續推廣TDengine,同時也願意為一些商業版功能付費,支持國產,支持濤思。
作者介紹
於淼,學歷碩士,副研究員,主要從事MES系統研發以及智能制造相關理論和標准研究,主要研究方向:數字工廠使能技術、制造執行系統關鍵技術和智能制造標准體系等,參與國家級項目及企業項目十余項,包括國家重點研發計划以及國家智能制造專項等。
雙匯發展多個分廠的能源管控大數據系統主要采用兩種技術棧:InfluxDB/Redis和Kafka/Redis/HBase/Flink,對於中小型研發團隊來講,無論是系統搭建,還是實施運維都非常棘手。經過對InfluxDB/Redis和TDengine大數據平台的功能和性能對比測試,最終將TDengine作為實施方案。
### 項目背景基於雙匯發展對能源管控的需求,利用雲平台技術以及電氣自動化處理手段,對雙匯發展的一級、二級、三級能源儀表進行整體改造,實現儀表組網,進一步通過邊緣網關進行能源在線監測數據的采集,並上報至雲平台,建立統一能源管理信息化系統,實現能源的實時監控、報表統計、能源流向分析與預測,降低企業單位產品能源消耗,提高經濟效益,最終實現企業能源精細化管理。
### 總體架構能源管控平台基於私有雲構建,包括完整的IaaS層、PaaS層和SaaS層,而能源采集系統作為管控平台中最為重要的一環,采用TDengine作為核心數據引擎,通過Restful接口進行儀表在線數據插入,並實現大規模時序數據的高效穩定存儲,同時,也為能源管控應用層提供實時數據查詢、歷史聚合統計、流計算和訂閱服務等功能,實現能源地圖監控、能耗預警、能源流向預測和能源互聯綜合決策,具體架構如下圖所示。### TDengine關鍵應用#### Connector選擇
本項目數據采集最關鍵的環節,就是將訂閱到的MQTT數據插入到TDengine中,於是也就涉及到了TDengine連接器的選擇,我們平時項目中java使用居多,而且JDBC的性能也相對較強,理論上,應該選擇JDBC API,但最終選擇了RESTful Connector,主要考慮以下幾點:
**1)簡潔性**
毫無疑問,RESTful通用性最強,TDengine直接通過HTTP POST 請求BODY中包含的SQL語句來操作數據庫,而且TDengine本身作為時序數據庫並不提供存儲過程或者事務機制,基本上都是每次執行單條SQL語句,所以RESTful在使用上很簡便。
**2)可移植性**
本項目的Java應用都是部署在Kubernetes中,所以向TDengine插入數據的Java應用需要容器化部署,而之前了解到,JDBC需要依賴的本地函數庫libtaos.so文件,所以容器化部署可能較為麻煩,而RESTful僅需采用OKHttp庫即可實現,移植性較強。
**3)數據規模**
本項目數采規模不大,大約每分鍾7000條數據,甚至后續數采功能擴展到其他分廠,RESTful也完全滿足性能要求。
但總體來講,JDBC是在插入與查詢性能上具有一定優勢的,而且支持從firstEp和secondEp選擇有效節點進行連接(類似於Nginx的keepalive高可用),目前TDengine版本發布情況上看,JDBC的維護與提升也是重中之重,后續項目也可能會向JDBC遷移。
#### RESTful代碼實現
**1)ThreadPoolExecutor線程池**
訂閱EMQX和RESTful插入TDengine的代碼寫在了同一個java服務中,每接收到一條MQTT訂閱消息,便開啟一個線程向TDengine插入數據,線程均來自於線程池,初始化如下:
```ExecutorService pool = new ThreadPoolExecutor(150, 300, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(100), Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardOldestPolicy());```
線程池采用基於數組的先進先出隊列,采用丟棄早期任務的拒絕策略,因為本次場景中單次RESTful插入數據量大約在100~200條,執行速度快,遲遲未執行完極可能是SQL語句異常或連接taosd服務異常等原因,應該丟棄任務。核心線程數設為150,相對較高,主要為了保證高峰抗壓能力。
**2)OKHttp線程池**
在每個ThreadPoolExecutor線程中,基於OKHttp庫進行RESTful插入操作,也是采用ConnectionPool管理 HTTP 和 HTTP/2 連接的重用,以減少網絡延遲,OKHttp重點配置如下:
```public ConnectionPool pool() { return new ConnectionPool(20, 5, TimeUnit.MINUTES);}```
即最大空閑連接數為20,每個連接最大空閑時間為5分鍾,每個OKHttp插入操作采用異步調用方式,主要代碼如下:
```public void excuteTdengineWithSqlAsync(String sql,Callback callback) { try{ okhttp3.MediaType mediaType = okhttp3.MediaType.parse("application/octet-stream"); RequestBody body = RequestBody.create(mediaType, sql); Request request = new Request.Builder() .url(tdengineHost) .post(body) .addHeader("Authorization", "Basic cm9vdDp0YW9zZGF0YQ==") .addHeader("cache-control", "no-cache") .build(); mOkHttpClient.newCall(request).enqueue(callback); } catch (Exception e) { logger.error("執行tdengine操作報錯:"+ e.getMessage()); }}```
**3)Java打包鏡像**
長期壓力測試顯示,每秒執行200次RESTful插入請求,單次請求包含100條數據,每條數據包含5組標簽,Java服務內存穩定在300M~600M。而且上述模擬規模僅針對單個Java應用而言,在Kubernetes可以跑多個這樣pod來消費不同的MQTT主題,所以並發能力完全夠用。打包鏡像時,堆內存最大值設為1024MB,主要語句為:
```ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-XX:MaxRAM=2000m","-Xms1024m","-jar","/app.jar"]```
#### 性能測試
**1)RESTful插入性能**
按照3.2小節中的RESTful代碼進行數據插入,Java程序和TDengine集群均運行在私有雲中,虛擬機之間配備萬兆光纖交換機,Java程序具體如3.2小節所示,TDengine集群部署在3個虛擬機中,配置均為1TB硬盤、12核、12GB(私有雲中CPU比較充裕,但內存比較緊張),經過大約三周的生產環境運行,性能總結如下:
表1 生產環境下RESTful插入性能測試| Insert 行數| Insert 語句字節數 | 耗時/ms||--|--|--||5 | 282 |3||33 | 2,136 |5||70 | 3,240 |8||156 | 9,205 |12|
生產環境下,單條插入性能極高,完全滿足需求,當然前期也進行過稍大規模的插入場景模擬,主要是基於2.0.4.0以后的版本,注意到2.0.4.0之前的TDengine版本RESTful的SQL語句上限為64KB。模擬環境下,RESTful插入性能非常優秀,具體如下表所示。
表2 模擬環境下RESTful插入性能測試|Java 並發客戶端數量| Insert 行數| Insert 語句字節數 | 耗時/ms||--|--|--|--||5 | 1萬 |600,000|260||5 | 2萬 |1,200,000|480||8 | 6萬 |3,600,000|700|
**2)RESTful查詢性能**
使用RESTful進行SQL查詢時,性能也是非常好,目前真實生產環境中,數據總量為800萬,相對單薄,所以查詢性能測試在模擬環境下進行,在8億數據量下,LAST_ROW函數可以達到10ms響應速度,count、interval、group by等相關函數執行速度均在百毫秒量級上。
#### 實施方案
本項目針對雙匯發展下屬的6個分廠(后續會繼續擴充)進行能源數據采集,大約1200多塊儀表(后續會繼續擴充),每塊儀表包括3至5個采集標簽,采集頻率均為1分鍾,數據接入規模不大。六個廠各自有獨立的租戶空間,為了方便各自的時序數據庫管理,同時也方便各廠間的聚合查詢(目前六個分廠均從屬雙匯發展總部),所以各分廠分別建立超級表,每個超級表包括4個tag,分別為廠編號、儀表級別、所屬工序和儀表編號,具體超級表建表情況如下圖所示。
主要用到的集群包括TDengine集群、EMQX集群和Redis集群,其中Redis集群在數據采集方面,僅僅用於緩存儀表連接狀態,其重點在於緩存業務系統數據;EMQX集群用於支撐MQTT數據的發布與訂閱,部署在Kubernetes中,可以實現資源靈活擴展;TDengine集群部署在IaaS虛擬機中,支持大規模時序數據的存儲與查詢。
表3 集群配置信息|集群名稱|部署規模|虛擬機數量| 虛擬機配置|--|--|--|--||TDengine|三節點分布式|3|CPU-12核 內存-12GB 存儲-1TB|EMQX|三節點分布式|3|CPU-8核 內存-12GB 存儲-500GB|Redis|一主兩從三哨兵|3|CPU-4核 內存-12GB 存儲-500GB
按照TDengine官方的建議,“一個數據采集點一張表,同一類型數據采集點一張超級表”,我針對不同分廠的水表、電表、蒸汽表和燃氣表分別建立的超級表,每個儀表單獨建表,保證每張表的時間戳嚴格遞增。在實踐TDengine的過程中,重點體會如下:
**1)集群搭建門檻低**
TDengine集群安裝部署非常便捷,尤其相比於其他集群,僅需要簡單的配置就可以實現生產環境級的搭建,官方文檔也比較豐富,社區活躍,也大為降低了后續運維成本。
**2)插入與查詢效率極高**
TDengine的插入與查詢性能極高,這點在實際運行時也深有感觸,用last_row函數查詢儀表最新數據,基本上可以達到毫秒級,在幾十億級的數據上進行聚合查詢操作,也可達到百毫秒級,極大提供了系統的響應速度。
**3)全棧式時序處理引擎**
在未使用TDengine之前,我們主要采用InfluxDB/Redis和Kafka/Redis/HBase/Flink兩種技術棧,對於我們中小型研發團隊來講,無論是系統搭建,還是實施運維都非常棘手。但是使用TDengine后,一切都簡化了,TDengine將數據庫、消息隊列、緩存、流式計算等功能融合一起,以一種全棧的方式,為我們的大數據系統帶來了便捷。技術方案的對比如表4所示。
注:方案一為InfluxDB/Redis,方案二為Kafka/Redis/HBase/Flink,方案三為TDengine
表4 數據采集方案對比|技術方案|說明|優點|缺點|--|--|--|--||方案一|實時數據存入Redis 歷史數據存入InfluxDB|部署易上手,社區成熟|1)InfluxDB查詢與插入效率不高,集群版收費 2)無法直接集成開源版EMQX Broker|方案二|將采集數據發布到Kafka,利用Flink將實時數據存入/Redis,歷史數據存入HBase|1)消息吞吐量大 2)流計算功能豐富,生態成熟 3)集群版開源|1)技術體系龐大,部署運維成本高 2)HBase插入性能可能無法滿足Kafka的吞吐量 3)無法直接集成開源版EMQX Broker|方案三|直接將采集數據存入TDengine|1)部署便捷,運維簡單 2)集群版開源 3)訂閱功能和流計算功能完善 4)插入與查詢效率極高 5)資源占用少 6)可與開源版EMQX Broker直接集成|暫時不支持時序數據的更新與刪除(后續會支持)
從表4的對比方案中可以看出,TDengine(方案三)是有着很大的優勢,尤其在開源EMQX Broker的支持上也非常好(主要依賴於Restful接口),其他的例如Kafka和InfluxDB只能和企業版EMQX集成;在數據插入和查詢效率方面,上述三種方案關鍵在於TDengine、HBase和InfluxDB的對比,官網有非常詳細的測試報告,TDengine也是有絕對優勢,這里就不過多敘述。所以選擇TDengine是勢在必行的。
#### 技術期望
在時序數據庫性能方面,TDengine有着很大的優勢,並且也集成了消息訂閱和流計算功能,可以說在中小型物聯場景下,是無需部署Kafka和Flink的。當然個人理解TDengine不是為了完全取代Kafka和Flink而生的,尤其是在大型雲服務項目中,更多是共存。
但是在邊緣端,TDengine憑借着極低的資源占用率和優秀的時序處理性能,將會產生更大的能量,期望能徹底集成邊緣流計算和MQTT broker等功能,擴充Modbus、OPC-UA等常見工業協議支持,向下連接工業設備或者物聯設施,向上和邊緣Kubernetes生態(如KubeEdge、K3S等)協同,或者直接和雲中心協同。
#### 系統運行界面
項目重點是能耗統計,而在線采集到TDengine里的數據都是累計量,所以在計算能耗時,需要在不同的超級表執行按表分組、按時間周期采樣的查詢,類似下面語法:```select last(累計列) as max_val,first(累計列) as min_val from [超級表名] where [標簽欄相關過濾] and ts>=now-10h INTERVAL(1h) group by [儀表編號] ;```得益於TDengine的極佳性能,基本能保證不超過百毫秒的訪問延時,下面是一些相關的PC端、移動端界面(我們移動端是用H5做的,為了直接能跑在Android和iOS上)。### 寫在最后其實從2019年開始就一直在關注TDengine,也看了很多陶總的演講,受益匪淺,尤其在今年8月份,TDengine進行了集群版開源,也正好准備啟動能源數據采集項目,所以果斷采用TDengine作為核心時序引擎,目前也是收獲了非常的效果。本次項目實施過程中,尤其感謝濤思數據的蘇曉慰工程師,多次協助解決TDengine相關的實施問題。計划在后續其他項目也也會繼續推廣TDengine,同時也願意為一些商業版功能付費,支持國產,支持濤思。
### 作者介紹於淼,學歷碩士,副研究員,主要從事MES系統研發以及智能制造相關理論和標准研究,主要研究方向:數字工廠使能技術、制造執行系統關鍵技術和智能制造標准體系等,參與國家級項目及企業項目十余項,包括國家重點研發計划以及國家智能制造專項等。