智汇工业-智慧工业、智能制造及工业智能、工业互联门户网站,专业的工业“互联网+”传媒

Android提高應用篇之模擬信號示波器

來源:網絡

點擊:2094

A+ A-

所屬頻道:新聞中心

關鍵詞: Android手機,模擬信號示波器

        本文結合SurfaceView實現一個Android版的手機模擬信號示波器(PS:以前也講過J2ME版的手機示波器)。最近物聯網炒得很火,作為手機軟件開發者,如何在不修改手機硬件電路的前提下實現與第三方傳感器結合呢?麥克風就是一個很好的ADC接口,通過麥克風與第三方傳感器結合,再在軟件里對模擬信號做相應的處理,就可以提供更豐富的傳感化應用。

        先來看看本文程序運行的效果圖(屏幕錄像速度較慢,真機實際運行起來會更加流暢):

     

           本文程序使用8000hz的采樣率,對X軸方向繪圖的實時性要求較高,如果不降低X軸的分辨率,程序的實時性較差,因此程序對X軸數據縮小區間為8倍~16倍。由于采用16位采樣,因此Y軸數據的高度相對于手機屏幕來說也偏大,程序也對Y軸數據做縮小,區間為1倍~10倍。在SurfaceView的OnTouchListener方法里加入了波形基線的位置調節,直接在SurfaceView控件上觸摸即可控制整體波形偏上或偏下顯示。

    main.xml源碼如下:

    view plaincopy to clipboardprint?
    <?xml version="1.0" encoding="utf-8"?> 
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        android:orientation="vertical" android:layout_width="fill_parent" 
        android:layout_height="fill_parent"> 
        <LinearLayout android:id="@+id/LinearLayout01" 
            android:layout_height="wrap_content" android:layout_width="fill_parent" 
            android:orientation="horizontal"> 
            <Button android:layout_height="wrap_content" android:id="@+id/btnStart" 
                android:text="開始" android:layout_width="80dip"></Button> 
            <Button android:layout_height="wrap_content" android:text="停止" 
                android:id="@+id/btnExit" android:layout_width="80dip"></Button> 
            <ZoomControls android:layout_width="wrap_content" 
                android:layout_height="wrap_content" android:id="@+id/zctlX"></ZoomControls> 
            <ZoomControls android:layout_width="wrap_content" 
                android:layout_height="wrap_content" android:id="@+id/zctlY"></ZoomControls> 
        </LinearLayout> 
        <SurfaceView android:id="@+id/SurfaceView01" 
            android:layout_height="fill_parent" android:layout_width="fill_parent"></SurfaceView> 
    </LinearLayout> 
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical" android:layout_width="fill_parent"
     android:layout_height="fill_parent">
     <LinearLayout android:id="@+id/LinearLayout01"
      android:layout_height="wrap_content" android:layout_width="fill_parent"
      android:orientation="horizontal">
      <Button android:layout_height="wrap_content" android:id="@+id/btnStart"
       android:text="開始" android:layout_width="80dip"></Button>
      <Button android:layout_height="wrap_content" android:text="停止"
       android:id="@+id/btnExit" android:layout_width="80dip"></Button>
      <ZoomControls android:layout_width="wrap_content"
       android:layout_height="wrap_content" android:id="@+id/zctlX"></ZoomControls>
      <ZoomControls android:layout_width="wrap_content"
       android:layout_height="wrap_content" android:id="@+id/zctlY"></ZoomControls>
     </LinearLayout>
     <SurfaceView android:id="@+id/SurfaceView01"
      android:layout_height="fill_parent" android:layout_width="fill_parent"></SurfaceView>
    </LinearLayout>
     

    ClsOscilloscope.java是實現示波器的類庫,包含AudioRecord操作線程和SurfaceView繪圖線程的實現,兩個線程同步操作,代碼如下:

    view plaincopy to clipboardprint?
    package com.testOscilloscope;  
    import java.util.ArrayList;  
    import android.graphics.Canvas;  
    import android.graphics.Color;  
    import android.graphics.Paint;  
    import android.graphics.Rect;  
    import android.media.AudioRecord;  
    import android.view.SurfaceView;  
    public class ClsOscilloscope {  
        private ArrayList<short[]> inBuf = new ArrayList<short[]>();  
        private boolean isRecording = false;// 線程控制標記  
        /** 
         * X軸縮小的比例 
         */ 
        public int rateX = 4;  
        /** 
         * Y軸縮小的比例 
         */ 
        public int rateY = 4;  
        /** 
         * Y軸基線 
         */ 
        public int baseLine = 0;  
        /** 
         * 初始化 
         */ 
        public void initOscilloscope(int rateX, int rateY, int baseLine) {  
            this.rateX = rateX;  
            this.rateY = rateY;  
            this.baseLine = baseLine;  
        }  
        /** 
         * 開始 
         *  
         * @param recBufSize 
         *            AudioRecord的MinBufferSize 
         */ 
        public void Start(AudioRecord audioRecord, int recBufSize, SurfaceView sfv,  
                Paint mPaint) {  
            isRecording = true;  
            new RecordThread(audioRecord, recBufSize).start();// 開始錄制線程  
            new DrawThread(sfv, mPaint).start();// 開始繪制線程  
        }  
        /** 
         * 停止 
         */ 
        public void Stop() {  
            isRecording = false;  
            inBuf.clear();// 清除  
        }  
        /** 
         * 負責從MIC保存數據到inBuf 
         *  
         * @author GV 
         *  
         */ 
        class RecordThread extends Thread {  
            private int recBufSize;  
            private AudioRecord audioRecord;  
            public RecordThread(AudioRecord audioRecord, int recBufSize) {  
                this.audioRecord = audioRecord;  
                this.recBufSize = recBufSize;  
            }  
            public void run() {  
                try {  
                    short[] buffer = new short[recBufSize];  
                    audioRecord.startRecording();// 開始錄制  
                    while (isRecording) {  
                        // 從MIC保存數據到緩沖區  
                        int bufferReadResult = audioRecord.read(buffer, 0,  
                                recBufSize);  
                        short[] tmpBuf = new short[bufferReadResult / rateX];  
                        for (int i = 0, ii = 0; i < tmpBuf.length; i++, ii = i  
                                * rateX) {  
                            tmpBuf[i] = buffer[ii];  
                        }  
                        synchronized (inBuf) {//  
                            inBuf.add(tmpBuf);// 添加數據  
                        }  
                    }  
                    audioRecord.stop();  
                } catch (Throwable t) {  
                }  
            }  
        };  
        /** 
         * 負責繪制inBuf中的數據 
         *  
         * @author GV 
         *  
         */  


        class DrawThread extends Thread {  
            private int oldX = 0;// 上次繪制的X坐標  
            private int oldY = 0;// 上次繪制的Y坐標  
            private SurfaceView sfv;// 畫板  
            private int X_index = 0;// 當前畫圖所在屏幕X軸的坐標  
            private Paint mPaint;// 畫筆  
            public DrawThread(SurfaceView sfv, Paint mPaint) {  
                this.sfv = sfv;  
                this.mPaint = mPaint;  
            }  
            public void run() {  
                while (isRecording) {  
                    ArrayList<short[]> buf = new ArrayList<short[]>();  
                    synchronized (inBuf) {  
                        if (inBuf.size() == 0)  
                            continue;  
                        buf = (ArrayList<short[]>) inBuf.clone();// 保存  
                        inBuf.clear();// 清除  
                    }  
                    for (int i = 0; i < buf.size(); i++) {  
                        short[] tmpBuf = buf.get(i);  
                        SimpleDraw(X_index, tmpBuf, rateY, baseLine);// 把緩沖區數據畫出來  
                        X_index = X_index + tmpBuf.length;  
                        if (X_index > sfv.getWidth()) {  
                            X_index = 0;  
                        }  
                    }  
                }  
            }  
            /** 
             * 繪制指定區域 
             *  
             * @param start 
             *            X軸開始的位置(全屏) 
             * @param buffer 
             *            緩沖區 
             * @param rate 
             *            Y軸數據縮小的比例 
             * @param baseLine 
             *            Y軸基線 
             */ 
            void SimpleDraw(int start, short[] buffer, int rate, int baseLine) {  
                if (start == 0)  
                    oldX = 0;  
                Canvas canvas = sfv.getHolder().lockCanvas(  
                        new Rect(start, 0, start + buffer.length, sfv.getHeight()));// 關鍵:獲取畫布  
                canvas.drawColor(Color.BLACK);// 清除背景  
                int y;  
                for (int i = 0; i < buffer.length; i++) {// 有多少畫多少  
                    int x = i + start;  
                    y = buffer[i] / rate + baseLine;// 調節縮小比例,調節基準線  
                    canvas.drawLine(oldX, oldY, x, y, mPaint);  
                    oldX = x;  
                    oldY = y;  
                }  
                sfv.getHolder().unlockCanvasAndPost(canvas);// 解鎖畫布,提交畫好的圖像  
            }  
        }  

    package com.testOscilloscope;
    import java.util.ArrayList;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Rect;
    import android.media.AudioRecord;
    import android.view.SurfaceView;
    public class ClsOscilloscope {
     private ArrayList<short[]> inBuf = new ArrayList<short[]>();
     private boolean isRecording = false;// 線程控制標記
     /**
      * X軸縮小的比例
      */
     public int rateX = 4;
     /**
      * Y軸縮小的比例
      */
     public int rateY = 4;
     /**
      * Y軸基線
      */
     public int baseLine = 0;
     /**
      * 初始化
      */
     public void initOscilloscope(int rateX, int rateY, int baseLine) {
      this.rateX = rateX;
      this.rateY = rateY;
      this.baseLine = baseLine;
     }
     /**
      * 開始
      *
      * @param recBufSize
      *            AudioRecord的MinBufferSize
      */
     public void Start(AudioRecord audioRecord, int recBufSize, SurfaceView sfv,
       Paint mPaint) {
      isRecording = true;
      new RecordThread(audioRecord, recBufSize).start();// 開始錄制線程
      new DrawThread(sfv, mPaint).start();// 開始繪制線程
     }
     /**
      * 停止
      */
     public void Stop() {
      isRecording = false;
      inBuf.clear();// 清除
     }
     /**
      * 負責從MIC保存數據到inBuf
      *
      * @author GV
      *
      */
     class RecordThread extends Thread {
      private int recBufSize;
      private AudioRecord audioRecord;
      public RecordThread(AudioRecord audioRecord, int recBufSize) {
       this.audioRecord = audioRecord;
       this.recBufSize = recBufSize;
      }
      public void run() {
       try {
        short[] buffer = new short[recBufSize];
        audioRecord.startRecording();// 開始錄制
        while (isRecording) {
         // 從MIC保存數據到緩沖區
         int bufferReadResult = audioRecord.read(buffer, 0,
           recBufSize);
         short[] tmpBuf = new short[bufferReadResult / rateX];
         for (int i = 0, ii = 0; i < tmpBuf.length; i++, ii = i
           * rateX) {
          tmpBuf[i] = buffer[ii];
         }
         synchronized (inBuf) {//
          inBuf.add(tmpBuf);// 添加數據
         }
        }
        audioRecord.stop();
       } catch (Throwable t) {
       }
      }
     };
     /**
      * 負責繪制inBuf中的數據
      *
      * @author GV
      *
      */
     class DrawThread extends Thread {
      private int oldX = 0;// 上次繪制的X坐標
      private int oldY = 0;// 上次繪制的Y坐標
      private SurfaceView sfv;// 畫板
      private int X_index = 0;// 當前畫圖所在屏幕X軸的坐標
      private Paint mPaint;// 畫筆
      public DrawThread(SurfaceView sfv, Paint mPaint) {
       this.sfv = sfv;
       this.mPaint = mPaint;
      }
      public void run() {
       while (isRecording) {
        ArrayList<short[]> buf = new ArrayList<short[]>();
        synchronized (inBuf) {
         if (inBuf.size() == 0)
          continue;
         buf = (ArrayList<short[]>) inBuf.clone();// 保存
         inBuf.clear();// 清除
        }
        for (int i = 0; i < buf.size(); i++) {
         short[] tmpBuf = buf.get(i);
         SimpleDraw(X_index, tmpBuf, rateY, baseLine);// 把緩沖區數據畫出來
         X_index = X_index + tmpBuf.length;
         if (X_index > sfv.getWidth()) {
          X_index = 0;
         }
        }
       }
      }


      /**
       * 繪制指定區域
       *
       * @param start
       *            X軸開始的位置(全屏)
       * @param buffer
       *            緩沖區
       * @param rate
       *            Y軸數據縮小的比例
       * @param baseLine
       *            Y軸基線
       */
      void SimpleDraw(int start, short[] buffer, int rate, int baseLine) {
       if (start == 0)
        oldX = 0;
       Canvas canvas = sfv.getHolder().lockCanvas(
         new Rect(start, 0, start + buffer.length, sfv.getHeight()));// 關鍵:獲取畫布
       canvas.drawColor(Color.BLACK);// 清除背景
       int y;
       for (int i = 0; i < buffer.length; i++) {// 有多少畫多少
        int x = i + start;
        y = buffer[i] / rate + baseLine;// 調節縮小比例,調節基準線
        canvas.drawLine(oldX, oldY, x, y, mPaint);
        oldX = x;
        oldY = y;
       }
       sfv.getHolder().unlockCanvasAndPost(canvas);// 解鎖畫布,提交畫好的圖像
      }
     }
    }
     

    testOscilloscope.java是主程序,控制UI和ClsOscilloscope,代碼如下:

    view plaincopy to clipboardprint?
    package com.testOscilloscope;  
    import android.app.Activity;  
    import android.graphics.Color;  
    import android.graphics.Paint;  
    import android.media.AudioFormat;  
    import android.media.AudioRecord;  
    import android.media.MediaRecorder;  
    import android.os.Bundle;  
    import android.view.MotionEvent;  
    import android.view.SurfaceView;  
    import android.view.View;  
    import android.view.View.OnTouchListener;  
    import android.widget.Button;  
    import android.widget.ZoomControls;  
    public class testOscilloscope extends Activity {  
        /** Called when the activity is first created. */ 
        Button btnStart,btnExit;  
        SurfaceView sfv;  
        ZoomControls zctlX,zctlY;  
          
        ClsOscilloscope clsOscilloscope=new ClsOscilloscope();  
          
        static final int frequency = 8000;//分辨率  
        static final int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;  
        static final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;  
        static final int xMax = 16;//X軸縮小比例最大值,X軸數據量巨大,容易產生刷新延時  
        static final int xMin = 8;//X軸縮小比例最小值  
        static final int yMax = 10;//Y軸縮小比例最大值  
        static final int yMin = 1;//Y軸縮小比例最小值  
          
        int recBufSize;//錄音最小buffer大小  
        AudioRecord audioRecord;  
        Paint mPaint;  
        @Override 
        public void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.main);  
            //錄音組件  
            recBufSize = AudioRecord.getMinBufferSize(frequency,  
                    channelConfiguration, audioEncoding);  
            audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency,  
                    channelConfiguration, audioEncoding, recBufSize);  
            //按鍵  
            btnStart = (Button) this.findViewById(R.id.btnStart);  
            btnStart.setOnClickListener(new ClickEvent());  
            btnExit = (Button) this.findViewById(R.id.btnExit);  
            btnExit.setOnClickListener(new ClickEvent());  
            //畫板和畫筆  
            sfv = (SurfaceView) this.findViewById(R.id.SurfaceView01);   
            sfv.setOnTouchListener(new TouchEvent());  
            mPaint = new Paint();    
            mPaint.setColor(Color.GREEN);// 畫筆為綠色    
            mPaint.setStrokeWidth(1);// 設置畫筆粗細   
            //示波器類庫  
            clsOscilloscope.initOscilloscope(xMax/2, yMax/2, sfv.getHeight()/2);  
              
            //縮放控件,X軸的數據縮小的比率高些  
            zctlX = (ZoomControls)this.findViewById(R.id.zctlX);  
            zctlX.setOnZoomInClickListener(new View.OnClickListener() {  
                @Override 
                public void onClick(View v) {  
                    if(clsOscilloscope.rateX>xMin)  
                        clsOscilloscope.rateX--;  
                    setTitle("X軸縮小"+String.valueOf(clsOscilloscope.rateX)+"倍" 
                            +","+"Y軸縮小"+String.valueOf(clsOscilloscope.rateY)+"倍");  
                }  
            });  
            zctlX.setOnZoomOutClickListener(new View.OnClickListener() {  
                @Override 
                public void onClick(View v) {  
                    if(clsOscilloscope.rateX<xMax)  
                        clsOscilloscope.rateX++;      
                    setTitle("X軸縮小"+String.valueOf(clsOscilloscope.rateX)+"倍" 
                            +","+"Y軸縮小"+String.valueOf(clsOscilloscope.rateY)+"倍");  
                }  
            });  
            zctlY = (ZoomControls)this.findViewById(R.id.zctlY);  
            zctlY.setOnZoomInClickListener(new View.OnClickListener() {  
                @Override 
                public void onClick(View v) {  
                    if(clsOscilloscope.rateY>yMin)  
                        clsOscilloscope.rateY--;  
                    setTitle("X軸縮小"+String.valueOf(clsOscilloscope.rateX)+"倍" 
                            +","+"Y軸縮小"+String.valueOf(clsOscilloscope.rateY)+"倍");  
                }  
            });  
              
            zctlY.setOnZoomOutClickListener(new View.OnClickListener() {  
                @Override 
                public void onClick(View v) {  
                    if(clsOscilloscope.rateY<yMax)  
                        clsOscilloscope.rateY++;      
                    setTitle("X軸縮小"+String.valueOf(clsOscilloscope.rateX)+"倍" 
                            +","+"Y軸縮小"+String.valueOf(clsOscilloscope.rateY)+"倍");  
                }  
            });  
        }  
        @Override 
        protected void onDestroy() {  
            super.onDestroy();  
            android.os.Process.killProcess(android.os.Process.myPid());  
        }  
          
        /** 
         * 按鍵事件處理 
         * @author GV 
         * 
         */ 
        class ClickEvent implements View.OnClickListener {  
            @Override 
            public void onClick(View v) {  
                if (v == btnStart) {  
                    clsOscilloscope.baseLine=sfv.getHeight()/2;  
                    clsOscilloscope.Start(audioRecord,recBufSize,sfv,mPaint);  
                } else if (v == btnExit) {  
                    clsOscilloscope.Stop();  
                }  
            }  
        }  
        /** 
         * 觸摸屏動態設置波形圖基線 
         * @author GV 
         * 
         */ 
        class TouchEvent implements OnTouchListener{  
            @Override 
            public boolean onTouch(View v, MotionEvent event) {  
                clsOscilloscope.baseLine=(int)event.getY();  
                return true;  
            }  
              
        }  

    (審核編輯: 智匯小新)

    聲明:除特別說明之外,新聞內容及圖片均來自網絡及各大主流媒體。版權歸原作者所有。如認為內容侵權,請聯系我們刪除。

    主站蜘蛛池模板: 企多网-B2B网站,B2B商务平台免费b2b,推荐注册送积分,免费注册企业商铺, | 消防排烟风机|防火阀|斜流风机|江苏恒恒暖通设备有限公司 | 兰州职业学校-新华互联网学校咨询平台中心 | 淮南网站制作丨淮南做网站丨淮南网络公司丨淮南哪家网络公司好丨淮南智讯网络 | 无铅锡膏,无铅锡膏厂家,有铅锡膏厂家,高温锡膏厂家,环保锡丝,贴片红胶-东莞市科舜电子科技有限公司 | 思沃普智能会议预约管理系统-视频会议管理-信息发布-访客管理-会议运维-会议支持-工位管理系统 | 湖南众一离心机股份有限公司_活塞推料离心机_沉降离心机_卧式刮刀离心机 | 郑州阳光房|封阳台|钢结构【河南郑州如意阳光房门窗有限公司】 | 全自动视觉点胶机|在线式点胶机|精密点胶阀设备厂家-杭州迈伺特科技有限公司 | 上海机械加工-机械加工-精密机械加工-上海欧野精工机械有限公司 上海慧泰仪器制造有限公司_一体型马弗炉-可控真空干燥箱-强光稳定性试验箱 | 石英砂|无烟煤滤料|火山岩|聚合硫酸铁|活性炭-河南碧水清源水处理材料有限公司 | 河北高新技术企业认定,沧州商标注册,沧州9001质量管理体系认证,沧州高新技术企业认定,沧州体系认证,沧州商标续展,沧州版权登记,河北国瑞企业管理咨询有限公司 | 连接器-连接线-连接器厂家-鑫鹏博20年高品质连接器生产厂家 | 微行科技(MicroX):半导体装备核心供应商-超高真空炉 | 智汇工业-智慧工业、智能制造及工业智能、工业互联门户网站,专业的工业“互联网+”传媒 | 永磁变频空压机-无油空压机-螺杆式空压机热能回收-空压机配套-空压机合同能源管理-维修保养-北京斯特兰压缩机有限公司 | 金煌家装官网-长沙装饰|长沙装修|长沙装修公司|长沙装饰公司-金煌家装 | 矿用三环链|锻打/焊接三环链|矿车万能环|三环链销子-济宁卓力工矿设备有限公司 | 上海钧尚电器有限公司 - Faulhaber电机 AMETEK pittman电机 AMETEK ROTRON军用航空风机 Exlar电动缸 MAE电机 MCG电机 CP电动工具 马头工具 AMCI驱动器 直流电机 减速箱 直流伺服电机,无刷电机,直线电机 直流防爆电机 防爆电机 汽车助力转向电机 EPS电机 faulhaber motor faulhaber gearbox NANOTEC电机 ELWOOD电机 PHYTRON电机 EXLAR伺服电动缸 高力矩、高性能直流电机,音圈电机,风机,直流风机,航空风机 | 明星代言,明星代言费,明星代言价格查询-良策明星经纪公司 | 雾度计-雾度仪-透光率测试仪-3nh品牌雾度仪生产厂家 | 削片机|木材破碎机|木材粉碎机|模板破碎机|双轴撕碎机_郑州木工机械制造厂 | 深圳潜水培训-潜水考证-公共安全潜水-救援潜水【深圳超潜潜水】 深圳汽车贴膜_深圳全车车身改色贴膜|UPPF隐形车衣官网 | 永磁变频空压机-无油空压机-螺杆式空压机热能回收-空压机配套-空压机合同能源管理-维修保养-北京斯特兰压缩机有限公司 | 挖掘机|小型挖掘机|挖掘机抓木机|轮式挖掘机|宝鼎挖掘机-宝鼎液压机械公司厂家直销 | 液晶拼接屏_液晶监视器_液晶广告机_触摸一体机_户外广告机_中亿睿企业官网 | 氯化氢钢瓶回收_氯化氢气体钢瓶回收_山东言赫化工有限公司 | 物联网环控器-智能养殖监控系统-智能化养殖控制器-养殖环境控制器-朗锐恒科技 | 意优教育|意大利留学中介_意大利留学费用_意大利申请条件_北京意大利语培训学校 | 乐清人才网_乐清招聘网_求职找工作平台 | 英格索兰隔膜泵_ARO气动隔膜泵_英格索兰隔膜泵配件【原厂正品】连续五年无投诉_英格索兰隔膜泵代理-苏州瑞晟茂环保设备有限公司 印刷公司,北京印刷厂,宣传画册手册印刷厂-和智印彩页设计 | 全自动灌装机械设备-迈驰粉末/颗粒/液体/膏体灌装机械设备流水线生产厂家 | 河南新百福国际物流有限公司| 轴流消防排烟风机,排烟防火阀厂家,铝合金百叶风口-德州正邦通风设备有限公司 | 友信京泰-操作台-调度台-控制台-监控台定制厂家 | 意大利留学-意大利语培训-马来西亚留学【长青藤海外】 | 氧化膜测厚仪-瓶壁测厚仪-QNIX菲尼克斯膜厚仪-深圳市时代之峰科技有限公司 | 环保除尘设备_燃气/燃油热水锅炉_光氧空气净化器_蒸汽玉米压片机_压片设备_烘干设备-山东金盾节能环保设备有限公司 | 排污管道疏通_长沙消防管道/暗管网漏水检测维修_长沙雨水管道疏通就找湖南鸿磊环保工程有限公司 排水PVC管-PVC排污管-给水PVC管-电线PVC管-米阳建材pvc管厂 | 火绒杀毒软件|火绒安全企业版_西南|四川|重庆|贵州|云南|西藏|成都火绒服务中心_成都火影科技有限公司-火绒安全|成都火影科技|火绒 | 家用油烟净化机_商用餐饮油烟净化器_工业油雾废气处理设备_深圳市宝篮环保 |