視圖庫過車數據模擬工具設計(下) 4. 關鍵算法 5. 運行方式

4. 關鍵算法

  生成行車軌跡的算法描述:對於每個過車agent,會隨機選擇一個車牌,隨機選擇一個卡口作爲起始卡口,生成一個行車軌跡。在所行駛的座標範圍內,在某個卡口點位可選擇的行駛方向如下圖所示:



  由圖4可知,在不同的座標可選擇的行駛方向也有不同,可選擇4個方向,也可能選擇3個方向,也可能選擇2個方向。行車軌跡生成算法需要根據當前卡口座標,在候選方向集合中隨機選擇一個方向來模擬行車。
  對於每個行車軌跡的長度,即經過的卡口點的數量,可隨機生成,建議最大距離爲行駛範圍矩形對角線的長度。
  行車軌跡模擬算法如下所示:

名稱:模擬行車軌跡
輸入參數:無
輸出參數:無
過程:
 //隨機選擇一個卡口
startTollgate = getRandomTollgate(); 
//隨機選擇一個車牌並鎖定該車牌
plate = getRandomPlate();
//將起始過車寫入過車數據流
motorvehicle = createMotorvehicle(startTollgate, plate);
writeMotorvehicleDatastream(motorvehicle);
//隨機生成行駛長度
distance = getRandomDistance();
//隨機生成行駛速度,在20~80中選個隨機數
speed = getRandomSpeed();
//計算模擬的每條過車記錄,並寫入過車數據流中
currentTollgate = startTollgate;
For(int i=0; i< distance; i++){
  //根據當前卡口能走的方向計算出隨機行駛方向
  direction = simuRandomDirection(currentTollgate);
  //根據速度計算出經過下一卡口的過車時間
  plate.lasttime = calcPassTime(speed);
  //根據當前卡口和行駛方向計算出下一卡口
  nextTollgate = calcNextTollgate(currentTollgate, direction);
  //將模擬的過車寫入過車數據流
  motorvehicle = createMotorvehicle(nextTollgate, plate);
  writeMotorvehicleDatastream(motorvehicle);
  //進入下一次循環
  currentTollgate = nextTollgate
}

5. 運行方式

  目前我們的視圖數據存儲使用hdfs和hive,建表工具使用iceberg。因此我們寫了一個flink job來寫入到iceberg的過車表中。flink job中自定義生成過車數據的source,過車寫入datastream中,最後寫入爲iceberg表的sink中。過車agent的source代碼如下:

/**
 * Title: CommonMTAgent.java
 * Description: 通用過車agent
 * author zhang.kai  
 * Copyright: Copyright (c) 2025
 * Company: www.kedacom.com 
 * date: 2021年7月21日-下午2:37:32
 * version: V1.0
 */
package com.kedacom.simumotor.agent;

import java.io.Serializable;
import java.util.Date;

import org.apache.flink.streaming.api.functions.source.SourceFunction.SourceContext;
import org.apache.flink.table.data.GenericRowData;
import org.apache.flink.table.data.RowData;
import com.kedacom.simumotor.config.SimuConfig;
import com.kedacom.simumotor.pojo.Motorvehicle;
import com.kedacom.simumotor.pojo.Plate;
import com.kedacom.simumotor.pojo.SimuTollgate;
import com.kedacom.simumotor.pool.PoolManager;
import com.kedacom.simumotor.util.MotorVehicleUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class CommonMTAgent implements Runnable, Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = -183630777130253681L;

    private SourceContext<RowData> sc;

    public String name;

    public CommonMTAgent() {

    }

    public CommonMTAgent(SourceContext<RowData> ctx) {
        sc = ctx;
    }

    public void start() {
        startSimuData();
    }

    @Override
    public void run() {
        log.info("************** plateLastAppearTime:{}",new Date(SimuConfig.plateLastAppearTime).toString());
        long count = 0;
        while (!SimuConfig.ifstop) {

            try {
                // 獲取隨機卡口和車牌
                SimuTollgate tollgate = PoolManager.getRandomTollgate();
                Plate plate = PoolManager.getRandomPlate();
                // 生成行車軌跡
                synchronized (plate) {
                    if (plate.getPlateLastTime() < System.currentTimeMillis()) {
                        plate.setPlateLastTime(System.currentTimeMillis());
                    }
                    // 在上次出現的時間上加上間隔
                    plate.setPlateLastTime(plate.getPlateLastTime() + SimuConfig.APPEAR_DELAY * 1000);
                    writeMotorvehicle2Stream(tollgate, plate);
                    count++;
                    // 生成隨機軌跡
                    int pathLength = MotorVehicleUtils.getRandomPathLength();

                    for (int i = 0; i < pathLength; i++) {
                        tollgate = MotorVehicleUtils.getNextTollgate(tollgate);
                        if (null == tollgate) {
                            break;
                        }

                        MotorVehicleUtils.setNextAppearTime(plate);
                        writeMotorvehicle2Stream(tollgate, plate);
                        count++;
                    }

                }
                // 到達批量寫入,進行計數
                if (count >= SimuConfig.BATCH_WRITE_SIZE) {
                    log.info("batchWriteMv:{}", count);
                    // Thread.sleep(10000);
                    count = 0;
                }
            } catch (Exception e) {
                log.error("XXXXXX CommonMTAgent run error", e);
            }
        }
    }

    /**
     * 啓動模擬數據線程
     */
    private void startSimuData() {
        Thread agentThread = new Thread(this);
        agentThread.setName("SimuThread-" + name);
        agentThread.start();
    }

    /**
     * 將生成的motorvehicle寫入數據流中
     * 
     * @param tollgate
     * @param plate
     * @param metricqueue
     */
    private void writeMotorvehicle2Stream(SimuTollgate tollgate, Plate plate) {
        Motorvehicle mv = MotorVehicleUtils.getMotorvehicle(tollgate, plate);
        RowData row = transformMv(mv);
        sc.collect(row);
    }

    /**
     * 將motorvehicle對象轉換爲
     * 
     * @param mv
     * @return
     */
    private RowData transformMv(Motorvehicle mv) {
        GenericRowData rowData = new GenericRowData(16);
        long passtime = mv.getPassTime() / 1000;
        int days = (int) (passtime / (60 * 60 * 24));
        rowData.setField(0, days);
        rowData.setField(1, MotorVehicleUtils.wrap(mv.getMotorVehicleID()));
        rowData.setField(2, mv.getInfoKind());
        rowData.setField(3, MotorVehicleUtils.wrap(mv.getTollgateId()));
        rowData.setField(4, passtime);
        rowData.setField(5, MotorVehicleUtils.wrap(mv.getDeviceId()));
        rowData.setField(6, MotorVehicleUtils.wrap(mv.getStorageUrl1()));
        rowData.setField(7, mv.getLeftTopX());
        rowData.setField(8, mv.getLeftTopY());
        rowData.setField(9, mv.getRightBtmX());
        rowData.setField(10, mv.getRightBtmY());
        rowData.setField(11, MotorVehicleUtils.wrap(mv.getHasPlate()));
        rowData.setField(12, MotorVehicleUtils.wrap(mv.getPlateClass()));
        rowData.setField(13, MotorVehicleUtils.wrap(mv.getPlateColor()));
        rowData.setField(14, MotorVehicleUtils.wrap(mv.getPlateNo()));
        rowData.setField(15, MotorVehicleUtils.wrap(mv.getVehicleColor()));
        return rowData;
    }

}

  經過測試,在我們自己的flink集羣上,每小時能夠寫入約2億條過車數據,一天大約能夠寫入近50億過車數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章