数据中台

数据中台,负责接入设备,负责基本的设备管理、数据管理、项目管理功能,告警功能等

背景

数据是水利智慧化、数字化的基石,数据中台是承上启下的作用。对下,需要稳定接入存量的采集终端设备。对上,提供标准化的数据接口,为水文、雨水情监测、灌区、防汛、山洪灾害预警、生态流量监测、大坝安全监测、水库除险加固、水质监测等应用场景提供数据底座。

现状:虽然各级部门如水文部门、防汛部门、生态环保部门等都有数据中台,但是很多平台已经运行了多年,已经过了维护期,有如下一些问题:

  • 协议的理解不一致,导致对通讯协议支持的不完整。
  • 受限于当时上线时对于设备数量的预估不足,平台的并发能力无法支撑更多的设备接入。
  • 缺少安全接入、安全传输相关的能力。
  • 缺少设备运维管理能力,设备在线率低,维护成本高。
  • 对于数据的存储和分析能力有限
  • 数据孤岛,无法与上级平台、其他水利数据
  • 数据质量参差不齐。

产品特点

为了解决上述问题,我们开发了一站式数据中台,用于智慧水利领域的数据底座。有如下特点:

  • 多个行业协议完整实现,兼容性强。支持的协议包括:
    • 《SL651 水文监测数据通信规约》
    • 《SL427 水资源监控数据传输规约》
    • 《SZY206 水资源监控数据传输规约》
    • 《湖南省小型水库雨水情测报和大坝安全监测设施建设与运行技术指南》
    • 自定义 MQTT 协议。
    • 支持按需的加密规范、接入鉴权规范
    • 支持对原始报文的记录、查询、重放、解析等调试功能。
  • 一个平台管理所有厂家设备:支持各品牌设备接入。快速融合智慧水利行业的多源异构数据,解决数据孤岛问题和历史设备数据无法迁移的问题。
  • 安全增强。强化了设备接入安全、传输安全、数据备份等。
  • 数据管理功能。支持数据存储、数据导入、数据导出、数据统计分析等功能。
  • 开放性。支持对外提供数据服务,提供标准的数据拉取和推送接口,实现数据的无缝融合。
  • 提供数据质量、设备在线率的管理。
  • 平台支持多项目、多租户、多角色,支持细粒度的权限管理。也支持按照区域划分的权限管理。
  • SaaS 平台对合作伙伴免费开放使用。

测站管理

从拓扑结构的角度,有如下相关概念:

  • 项目:一般是指一个灌区、一个水域、一个或者多个水库、河流等。一个项目会有一个整体视图。
  • 测站:一般指一个遥测终端
  • 测点:一般指一个传感器,通过遥测终端采集一组数据并上报。
  • 传感器:采集一个或者一组数据。

一个项目可以有多个测站,一个测站对应了一台遥测终端机,但是对应多个不同类型、不同数量的测点。

  • 项目管理:项目档案、项目大屏等
  • 测站管理:测点管理、数据分析、数据导出、报表管理、测点布设(2D、3D)、告警配置、参数设置等
  • 告警管理:支持对每一个测点数据进行预警设置,实时告警信息可直接推送到责任人。
  • 运维管理:对于数据中台,我们需要了解站点的更多状态,比如测站的数据是否有漏报、缺报,是否有故障、失联等问题。同步我们也根据区域和承建商,对设备状态进行统计,方便用户更好的了解设备的状态、安排维护工作。

测站创建

有三种方式添加测站:

1、现场建站

手机 APP 的【建站】,选择采集要素(如果列表没有的采集要素,需到 web 平台上手动创建测点)。手机 APP 会自动获取当前坐标。如下图。

2、远程建站

现场确保设备上线之后,可以在平台上远程建站,功能路径:【后台管理】—— 【添加测站】。建站的时候,可以如下图选择一些简单的测点类型(也可以手动去测站详情里面添加测点)。

3、批量建站

对于有一些项目,会涉及到较多的测站、测点、传感器参数等的管理,为了方便项目管理,提高交付效率,我们支持通过 excel 导入的方式来批量的添加测站信息。

通过 excel 表,方便批量创建测站、添加测点。功能路径:【后台管理】—— 【基础数据】。注意事项:

  • 基础数据表格是存放在临时表中,每次上传文件都会覆盖这个表。
  • 基础数据中,标注了红色*的,是必填项。
  • 同步数据:基于上传的基础数据,添加测站、测点。有两种方案:
    • 追加:如果测站或者测点不存在,就添加,如果存在,则忽略。
    • 替换:如果测站或者测点不存在,就添加,如果存在,则替换。
  • 同步数据的时候,会先做检查,如数据检查失败,会退出同步过程,并返回提示信息。

目前支持三种基础数据的导入:

  • 支持 SL651 的测站数据批量管理:只提供一个 excel 文件即可,包括了测站编码、设备信息、测点信息、经纬度、行政区域等字段。
  • 支持 振弦式遥测终端 和 传感器的批量管理:需提供 振弦式遥测终端表、测点位置表、测点传感器参数表等。
  • 支持湖南小水库协议的数据批量管理:包括测站信息、水库信息、摄像头信息、测压管信息、渗流信息、表面水平位移信息、表面垂直位移信息等各种表。

如果上述表不能满足您的项目需要,可以联系我们技术支持,我们会帮您添加支持。

数据接入

数据接入需适配各个规约、地方协议,实现各厂家设备顺利接入:

  • 支持多种数据协议。支持包括标准水文协议、水资源协议(SL427和 SZY206)、湖南小水库协议等。并可以按需扩展其他协议或者地方协议。
  • 数据存储:数据接入层对数据进行标准化和结构化处理,存入时序数据库。
  • 数据统计分析:支持数据特征值提取、重采样、相关性分析、数据分布、数据报表输出等各种分析功能。
  • 数据开放平台:提供数据访问接口,方便第三方应用快速对接。

如下图是数据解析调试工具:

告警管理

支持对每一个测点的采集要素单独设置告警阈值。告警消息支持推送到责任人手机。

具体操作方式如下示意图:

异常数据处理

平台会提供基础的异常数据处理功能,用户可以选择是否开启异常数据自动处理流程,或者选择异常值拦截上报,由人工来处理。

系统支持如下几种情况:

1、时间整编

  • 将 RTU(远程终端单元)上传的原始数据时间精确整编成每 5 分钟一个的时间节点,从而便于后续的数据分析、处理及报告生成。

2、数据缺失

  • 通过灵活配置补数规则,平台能够智能识别数据中的缺失部分。针对缺数站点,系统会根据用户预设的逻辑和算法实时进行数据补全。确保数据的完整性和连续性。

3、基值调整

  • 平台支持基值调整功能,可以对采集的原始数据进行基值校正。这一机制确保了存入本地平台及数据库的数据是经过调整后的真实反映,有效消除了因传感器初始偏差或环境因素导致的测量误差。

4、数据修正

通过灵活配置的补数规则,平台能够智能识别并处理数据的缺失部分和异常部分。

  • 变化超限检查。平台提供设置变化超限阈值的功能。当收到传感器数据后,程序会将此数据和最近的 10 条数据分别比较,若此数据和其中任一条数据的差值小于等于此设置值,那么认为此数据是正常的,否则认为此数据异常。
  • 数据越限检查。平台中提供配置数据的上下限值的功能。当发现数据超出预设范围,系统将立即启动异常数据处理流程,包括标记、隔离或修正这些异常值,并实时通知相关人员。

其次,平台也从其他方面对故障和异常进行管理,包括:

  • 通信异常告警。如出现缺包、缺项等异常,辅助用户发现并定位信道问题,及时采取措施保障数据采集的连续性和稳定性。
  • 系统异常告警。当出现系统资源占用异常、服务状态异常等问题(如数据库连接异常、写入和查询异常等),系统将立即触发告警,在平台页面通知用户。并及时处理(12 小时内)。
  • 测站异常告警。主要是测站工况信息监测,如信号弱、电压过低或数据采集异常等,立即上报故障并告警,迅速引起管理人员的注意,便于及时排查并处理故障,确保遥测站的稳定运行。

总之,在数据的整个采集和传输的链路上,都应该对数据可靠性进行识别和处理。

  • 传感器端:应该采购专业的厂家生产的传感器。做好原始数据的采集和处理。
  • 在遥测终端,可对采集的数据进行初步的检查。当识别到异常,会先重试采集,如果数据依然异常,则上报无效数据和传感器故障。
  • 在平台侧,有如下工作:
    • 系统监控各个测站的运行状态,包括超时、在线状态、电压水平、充电情况、上报的故障等。及时发现和解决问题。
    • 系统对异常数据进行自动化处理,或者进行拦截,由人工来处理。

数据存储

支持关系行数据库 PostgreSQL。支持时序数据库,包括 tdengine、influxdb等。存储数据种类包括:

  • 监测数据,包括实时降雨、水位、流量、图像、安全监测数据等。
  • 水文气象数据,包括雨量、水位、流量、历史降雨等。
  • 泵闸数据,包括实时水位、实时流量、闸泵工情监控数据、机组状态、转速监测、闸门开度、视频监控等。
  • 运行管理数据,包括人员巡检记录、隐患上报流程、物资管理信息、运行维护工单、值班排班等。也包括资产管理相关数据,为运维功能提供辅助。
  • 地理信息数据,包括水库、灌区、电排站、河道等专题图层。

数据库也记录了所有设备的原始报文、参数配置、人员登录日志、操作日志等,以便更好的为业务系统服务。

数据分析

  • 数据可视化:支持不同时间范围、时间窗口的重采样查询,基于图表、表格的方式对数据进行显示。
  • 数据分析:支持灵活组合多个数据点,进行数据相关性、数据分布、数据趋势等分析,以及数据聚合、数据特征值提取等。
    • 时间整编:对实时数据、时段数据和历史数据进行统一整编,得到整编后的 5 分钟原始数据
    • 数据聚合:对原始数据使用不同时段做聚合,得到小时数据、日数据、旬数据、月数据和年数据,
    • 特征值提取:根据瞬时值、累计值等不同测值类型进行处理,得到瞬时值的平均、最大、最小等特征值统计,得到累计值的时段差值、平均差值、最大差值等特征值的统计。
  • 预警管理:根据预设的阈值和判断逻辑,进行预警管理。
  • 自动化报表:自动生成报表。支持用户自定义的报表模板。
  • 数据导出:支持数据导出到 Excel 等格式。
  • 文件管理:支持导出数据、报表数据、导入数据的存储管理。

开放数据接口

平台支持提供 HTTP 接口,方便数据共享。也支持推送数据(需适配对接协议)。

接口鉴权

为保证数据安全,在使用数据接口的时候,需按照下述鉴权方式进行鉴权。

平台会为每一个信息使用部门,提供一个 AppKey 和 AppSecret。

下面是鉴权方式:

签名算法使用 HmacSha256,签名结果使用 hexString 的方式转为字符串

const (
	headerAppKey    = "appkey"
	headerNonce     = "nonce"     // 随机字符串,长度是 10 ~ 30
	headerTimestamp = "timestamp" // unix时间戳(字符串),单位秒,请求的时候会检查时间戳,如果与服务器时间差异超过 10 秒,则认为请求非法
	headerSign      = "sign"      // 签名结果
)

读取请求头,得到 AppKey、nonce、timestamp、sign
将前三个数据按顺序(appkey=xxx,nonce=xxx,timestamp=xxx),连接起来,使用 AppSecret 签名,得到sign 进行对比,一致则说明鉴权通过

下面是不同语言实现的签名鉴权逻辑:

查看代码
#!/usr/bin/env python3
# coding=utf-8

import hmac
import hashlib
import random
import string
import json
import time


# 北向接口的鉴权
# Authentication headers
HEADER_APP_KEY = "appkey"
HEADER_NONCE = "nonce"
HEADER_TIMESTAMP = "timestamp"
HEADER_SIGN = "sign"

def generate_nonce(length=16):
    """Generate a random nonce string between 10-30 characters"""
    chars = string.ascii_letters + string.digits
    return ''.join(random.choice(chars) for _ in range(length))

def generate_signature(appKey, nonce, timestamp, appSecret):
    """Generate HMAC-SHA256 signature for authentication"""
    # Create the message string in the required format
    message = f"appkey={appKey},nonce={nonce},timestamp={timestamp}"
    
    # Create HMAC-SHA256 signature
    hmac_obj = hmac.new(appSecret.encode('utf-8'), 
                        message.encode('utf-8'),
                        hashlib.sha256)
    return hmac_obj.hexdigest()

def verify_auth(headers, appSecret):
    """Verify the authentication headers"""
    try:
        appKey = headers.get(HEADER_APP_KEY)
        nonce = headers.get(HEADER_NONCE)
        timestamp = headers.get(HEADER_TIMESTAMP)
        sign = headers.get(HEADER_SIGN)

        # Check if all required headers are present
        if not all([appKey, nonce, timestamp, sign]):
            return False, "Missing required headers"

        # 时间戳误差不超过 60 秒
        try:
            ts = int(timestamp)
            current_time = int(time.time())
            if abs(current_time - ts) > 60:
                return False, "Timestamp expired"
        except ValueError:
            return False, "Invalid timestamp format"

        # nonce 长度 
        if not (10 <= len(nonce) <= 30):
            return False, "Invalid nonce length"

        # Generate and verify signature
        expected_sign = generate_signature(appKey, nonce, timestamp, appSecret)
        if sign != expected_sign:
            return False, "Invalid signature"

        return True, "Authentication successful"
    except Exception as e:
        return False, f"Authentication error: {str(e)}"

def create_auth_headers(appKey, appSecret):
    """Create authentication headers for a request"""
    nonce = generate_nonce()
    timestamp = str(int(time.time()))

    sign = generate_signature(appKey, nonce, timestamp, appSecret)


    headers = {
        HEADER_APP_KEY: appKey,
        HEADER_NONCE: nonce,
        HEADER_TIMESTAMP: timestamp,
        HEADER_SIGN: sign
    }
    print("--------------------------------request header")
    print(json.dumps(headers, indent=4))
    print("--------------------------------")
    return headers


def test_north_auth():
    appKey = "d1tg4jnpw8cn"
    appSecret = "FW4rDB8vr0ofHXvWsXTeO8LxAPDCwZvz"
    headers = create_auth_headers(appKey, appSecret)
    status, message = verify_auth(headers, appSecret)
    print(message)

# 执行主函数
if __name__ == "__main__":
    test_north_auth()
package com.example.northauth;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.json.JSONObject;

public class NorthAuth {

    private static final String HEADER_APP_KEY = "appkey";
    private static final String HEADER_NONCE = "nonce";
    private static final String HEADER_TIMESTAMP = "timestamp";
    private static final String HEADER_SIGN = "sign";


    // 测试的时候,请更换这些参数
    String appKey = "d1tg4jnpw8cn";
    String appSecret = "FW4rDB8vr0ofHXvWsXTeO8LxAPDCwZvz";
    private static final  String hostUrl =  "https://nuc.beithing.com:60080";

    // 随机字符串
    public static String generateNonce(int length) {
        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuilder nonce = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            nonce.append(chars.charAt(random.nextInt(chars.length())));
        }
        return nonce.toString();
    }

    // 签名
    public static String generateSignature(String appKey, String nonce, String timestamp, String appSecret) throws Exception {
        String message = String.format("appkey=%s,nonce=%s,timestamp=%s", appKey, nonce, timestamp);
        Mac sha256Hmac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKey = new SecretKeySpec(appSecret.getBytes("UTF-8"), "HmacSHA256");
        sha256Hmac.init(secretKey);
        byte[] hash = sha256Hmac.doFinal(message.getBytes("UTF-8"));
        return bytesToHex(hash);
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }

    public static Map<String, String> createAuthHeaders(String appKey, String appSecret) throws Exception {
        String nonce = generateNonce(16);
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String sign = generateSignature(appKey, nonce, timestamp, appSecret);

        Map<String, String> headers = new HashMap<>();
        headers.put(HEADER_APP_KEY, appKey);
        headers.put(HEADER_NONCE, nonce);
        headers.put(HEADER_TIMESTAMP, timestamp);
        headers.put(HEADER_SIGN, sign);

        return headers;
    }

    // get请求
    public static void sendHttpGetRequest(String appKey, String appSecret, String url, Map<String, String> queryParams) {
        try {
            Map<String, String> headers = createAuthHeaders(appKey, appSecret);
            
            // 构建URL和查询参数
            StringBuilder urlBuilder = new StringBuilder(hostUrl + url);
            if (queryParams != null && !queryParams.isEmpty()) {
                urlBuilder.append("?");
                boolean first = true;
                for (Map.Entry<String, String> param : queryParams.entrySet()) {
                    if (!first) {
                        urlBuilder.append("&");
                    }
                    urlBuilder.append(URLEncoder.encode(param.getKey(), "UTF-8"))
                            .append("=")
                            .append(URLEncoder.encode(param.getValue(), "UTF-8"));
                    first = false;
                }
            }
            
            String urlString = urlBuilder.toString();
            URI uri = new URI(urlString);
            URL urlObj = uri.toURL();
            HttpURLConnection connection = (HttpURLConnection) urlObj.openConnection();
            connection.setRequestMethod("GET");

            // 设置请求头
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                connection.setRequestProperty(entry.getKey(), entry.getValue());
            }

            // 获取响应
            int responseCode = connection.getResponseCode();
            System.out.println("URL: " + urlString);
            System.out.println("Response Code: " + responseCode);

            BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String inputLine;
            StringBuilder response = new StringBuilder();

            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }
            in.close();

            // 解析并格式化输出JSON
            JSONObject jsonResponse = new JSONObject(response.toString());
            System.out.println("Response: " + jsonResponse.toString(4));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static void northPing(String appKey, String appSecret) {
        Map<String, String> params = new HashMap<>();
        sendHttpGetRequest(appKey, appSecret, "/api/north/ping", params);
    }

    public static void northStationInfo(String appKey, String appSecret, String stationSn) {
        Map<String, String> params = new HashMap<>();
        params.put("station_sn", stationSn);
        sendHttpGetRequest(appKey, appSecret, "/api/north/station_info", params);
    }

    public static void northStationData(String appKey, String appSecret, String stationSn, String measure, String timeWindow, long start, long end) {
        Map<String, String> params = new HashMap<>();
        params.put("station_sn", stationSn);
        params.put("measure", measure);
        params.put("time_window", timeWindow);
        params.put("start", String.valueOf(start));
        params.put("end", String.valueOf(end));
        sendHttpGetRequest(appKey, appSecret, "/api/north/station_data", params);
    }
    public static void main(String[] args) {
        // 生成接口签名信息
        try {
            System.out.println("\n测试签名接口:");

            Map<String, String> headers = createAuthHeaders(appKey, appSecret);
            // Convert headers map to JSON for pretty printing
            JSONObject jsonHeaders = new JSONObject(headers);
            System.out.println("Generated Headers: " + jsonHeaders.toString(4));
        } catch (Exception e) {
            e.printStackTrace();
        }


        // PING操作,检查鉴权
        try {
            System.out.println("\n检查接口鉴权:");
            northPing(appKey, appSecret);
        } catch (Exception e) {
            e.printStackTrace();
        }



        // 查询某个测站的信息
        // try {
        //     System.out.println("\n查询测站信息:");
        //     String stationSn = "0075685515";
        //     northStationInfo(appKey, appSecret,stationSn);
        // } catch (Exception e) {
        //     e.printStackTrace();
        // }

        // 查询此侧站某个测点的数据
        // URL:  /api/north/station_data
        // param :  {'station_sn': '0075685515', 'measure': 'vwp_osm_water_3', 'time_window': '1h', 'start': 1735700455, 'end': 1735786855}
        // 查询此测站某个测点的数据
        // try {
        //     System.out.println("\n查询测站时序数据:");

        //     String stationSn = "0075685515";
        //     String measure = "vwp_osm_water_3";
        //     String timeWindow = "1h";
        //     long start = 1735700455;
        //     long end = 1735786855;
            
        //     northStationData(appKey, appSecret, stationSn, measure, timeWindow, start, end);
        // } catch (Exception e) {
        //     e.printStackTrace();
        // }
    }
}

接口一、获取测站列表

分页查找当前应用有权限的测站列表。

参数:

  • page 分页查找的页序号(从 1 开始计数)
  • page_size 页大小,默认 10
查看代码
def north_station_list():
    headers = create_auth_headers(appKey, appSecret)
    GET("/api/north/station_list",headers, params= {
        "page":1,
        "page_size": 10,
    })

返回值:

查看代码
{
    "code": 0,
    "message": "",
    "payload": {
        "page": 1,
        "page_size": 10,
        "total": 8,
        "list": [
            {
                "station_sn": "AA99200300",
                "name": "测试 427",
                "category": "水库湖泊",
                "address": "广州市海珠区",
                "lat": "23.077200",
                "lng": "113.266700"
            },
            {
                "station_sn": "0012340001",
                "name": "测试 0001",
                "category": "水库湖泊",
                "address": "",
                "lat": "",
                "lng": ""
            },
            {
                "station_sn": "0075663835",
                "name": "测试3835",
                "category": "水库湖泊",
                "address": "深圳市南山区",
                "lat": "22.589414",
                "lng": "113.942558"
            },
            {
                "station_sn": "0012340086",
                "name": "测试 651",
                "category": "水库湖泊",
                "address": "广东省深圳市南山区",
                "lat": "22.592530",
                "lng": "113.943100"
            },
            {
                "station_sn": "0623646239",
                "name": "黄吉湾水 222",
                "category": "水库湖泊",
                "address": "广州市海珠区",
                "lat": "23.084530",
                "lng": "113.272480"
            },
            {
                "station_sn": "0621646247",
                "name": "准心坡水库",
                "category": "水库湖泊",
                "address": "深圳市南山区",
                "lat": "",
                "lng": ""
            },
            {
                "station_sn": "0623646238",
                "name": "杨家冲水库",
                "category": "水库湖泊",
                "address": "深圳市南山区",
                "lat": "",
                "lng": ""
            },
            {
                "station_sn": "0075685515",
                "name": "5515",
                "category": "水库湖泊",
                "address": "广东省深圳市福田区",
                "lat": "22.631600",
                "lng": "114.001050"
            }
        ],
        "len": 8
    }
}

接口二、获取测站详情

参数:

  • station_sn, 测站十位编码
查看代码
def north_station_info():
    headers = create_auth_headers(appKey, appSecret)
    GET("/api/north/station_info",headers, params= {
        "station_sn": "0075685515",
    })

返回值:

查看代码
appkey :  d1tg4jnpw8cn
nonce :  OHJtupAepc4beuyW
timestamp :  1735786855
sign :  376402cffb3f0cfebcfda74f49240581d5e90359130af54e14f70dceaa8b952e
URL:  https://nuc.beithing.com:60080/api/north/station_info
param :  {'station_sn': '0075685515'}

{
    "code": 0,
    "message": "success",
    "payload": {
        "name": "5515",
        "station_sn": "0075685515",
        "flag": "未知",
        "device_sn": "",
        "latitude": "22.631600",
        "longitude": "114.001050",
        "contractor_name": "小水库承建商",
        "data_points": [
            {
                "name": "水位",
                "measure": "water"
            },
            {
                "name": "电源电压",
                "measure": "battery"
            },
            {
                "name": "渗压计1",
                "measure": "vwp_osm_water_1"
            },
            {
                "name": "渗压计2",
                "measure": "vwp_osm_water_2"
            },
            {
                "name": "渗压计3",
                "measure": "vwp_osm_water_3"
            },
            {
                "name": "温度计",
                "measure": "rt_meter_1"
            }
        ]
    }
}

接口三、获取测点数据

参数

  • station_sn, 测站十位编码
  • measure 测点标识,在测站详情里面,会列出此测站所有测点信息。取 measure 字段即可
  • start 和 end,是时间范围,使用 unix 时间戳,单位是秒。
查看代码
def north_station_data():
    app_key = "d1tg4jnpw8cn"
    app_secret = "FW4rDB8vr0ofHXvWsXTeO8LxAPDCwZvz"
    headers = create_auth_headers(app_key, app_secret)
    # 最近一天的数据
    start, end = get_time_range("1d")
    GET("/api/north/station_data",headers, params= {
        "station_sn": "0075685515",
        "measure": "vwp_osm_water_3",
        "start": start,
        "end": end,
    })
    

返回值:

返回值中,header部分是每一个字段的名称、单位信息,可用于显示

查看代码

app_key :  d1tg4jnpw8cn
app_secret :  FW4rDB8vr0ofHXvWsXTeO8LxAPDCwZvz
nonce :  CGGANncmxLA0ll8n
timestamp :  1735786855
sign :  bad50c2273289a962e6ab2123d7c0fef4155617fabc9e8e082e65e82ce873ae6
URL:  https://nuc.beithing.com:60080/api/north/station_data
param :  {'station_sn': '0075685515', 'measure': 'vwp_osm_water_3','start': 1735700455, 'end': 1735786855}

响应数据:

{
    "code": 0,
    "message": "success",
    "payload": {
        "header": {
            "f": "频率(F)",
            "t": "温度(℃)",
            "timestamp": "时间",
            "value": "渗压水位(m)"
        },
        "len": 24,
        "list": [
            {
                "f": 5404.16,
                "t": 21.67,
                "timestamp": 1735704000,
                "value": 9.85
            },
            {
                "f": 5404.86,
                "t": 21.75,
                "timestamp": 1735707600,
                "value": 9.84
            },
            {
                "f": 5405.78,
                "t": 22.05,
                "timestamp": 1735711200,
                "value": 9.82
            },
            {
                "f": 5405.46,
                "t": 22.37,
                "timestamp": 1735714800,
                "value": 9.82
            },
            {
                "f": 5405.79,
                "t": 22.53,
                "timestamp": 1735718400,
                "value": 9.81
            },
            {
                "f": 5405.74,
                "t": 22.61,
                "timestamp": 1735722000,
                "value": 9.81
            },
            {
                "f": 5405.18,
                "t": 22.62,
                "timestamp": 1735725600,
                "value": 9.82
            },
            {
                "f": 5404.84,
                "t": 22.55,
                "timestamp": 1735729200,
                "value": 9.82
            },
            {
                "f": 5405.03,
                "t": 22.43,
                "timestamp": 1735732800,
                "value": 9.82
            },
            {
                "f": 5404.69,
                "t": 22.32,
                "timestamp": 1735736400,
                "value": 9.83
            },
            {
                "f": 5404.73,
                "t": 22.19,
                "timestamp": 1735740000,
                "value": 9.83
            },
            {
                "f": 5404.96,
                "t": 22.05,
                "timestamp": 1735743600,
                "value": 9.83
            },
            {
                "f": 5404.66,
                "t": 21.91,
                "timestamp": 1735747200,
                "value": 9.84
            },
            {
                "f": 5405.19,
                "t": 21.8,
                "timestamp": 1735750800,
                "value": 9.83
            },
            {
                "f": 5405.06,
                "t": 21.69,
                "timestamp": 1735754400,
                "value": 9.83
            },
            {
                "f": 5404.76,
                "t": 21.6,
                "timestamp": 1735758000,
                "value": 9.84
            },
            {
                "f": 5404.96,
                "t": 21.45,
                "timestamp": 1735761600,
                "value": 9.84
            },
            {
                "f": 5405.02,
                "t": 21.33,
                "timestamp": 1735765200,
                "value": 9.84
            },
            {
                "f": 5405.29,
                "t": 21.2,
                "timestamp": 1735768800,
                "value": 9.84
            },
            {
                "f": 5405.17,
                "t": 21.17,
                "timestamp": 1735772400,
                "value": 9.84
            },
            {
                "f": 5405.15,
                "t": 21.1,
                "timestamp": 1735776000,
                "value": 9.84
            },
            {
                "f": 5404.93,
                "t": 21.08,
                "timestamp": 1735779600,
                "value": 9.85
            },
            {
                "f": 5404.31,
                "t": 21.24,
                "timestamp": 1735783200,
                "value": 9.85
            },
            {
                "f": 5404.65,
                "t": 21.41,
                "timestamp": 1735786800,
                "value": 9.84
            }
        ]
    }
}
Last updated on March 24, 2025