305 lines
10 KiB
TypeScript
305 lines
10 KiB
TypeScript
import enhance from "../core/enhance.tsx";
|
||
import {Button, Checkbox, CheckboxChangeEvent, message, Select, Space, Switch, Tag, Typography} from "antd";
|
||
import {SettingOutlined} from "@ant-design/icons";
|
||
import axios from "axios";
|
||
import {baseURL, Prefix} from "../Global.tsx";
|
||
import React from "react";
|
||
import {Input_Port_Number, Input_String, Switch_Bool} from "../A_Global.tsx";
|
||
import { State_Shower } from "../State_Shower.tsx";
|
||
|
||
const {Option} = Select;
|
||
const { Text } = Typography;
|
||
|
||
class Output_Format {
|
||
type: string = "";
|
||
use_status: boolean = false;
|
||
mode_s_output_type: string = "";
|
||
use_mode_ac: boolean = false;
|
||
sbs_only_pos: boolean = false;
|
||
|
||
|
||
output(flush) {
|
||
return (<>
|
||
<Select
|
||
prefix={<Prefix label="包格式"/>}
|
||
defaultValue=""
|
||
value={this.type}
|
||
onChange={(value: string) => {
|
||
this.type = value;
|
||
flush();
|
||
}}
|
||
options={[
|
||
{value: 'BIN', label: 'BIN'},
|
||
{value: 'BIN_ID', label: 'BIN_ID'},
|
||
{value: 'AVR', label: 'AVR'},
|
||
{value: 'AVR_MLAT', label: 'AVR_MLAT'},
|
||
{value: 'SBS', label: 'SBS'},
|
||
]}
|
||
/>
|
||
{
|
||
(this.type == "BIN" || this.type == "BIN_ID") &&
|
||
<Checkbox checked={this.use_status} onChange={(e: CheckboxChangeEvent) => {
|
||
this.use_status = e.target.checked;
|
||
flush();
|
||
}}>状态消息</Checkbox>
|
||
}
|
||
|
||
{
|
||
(this.type == "SBS") &&
|
||
<Checkbox checked={this.sbs_only_pos} onChange={(e: CheckboxChangeEvent) => {
|
||
this.sbs_only_pos = e.target.checked;
|
||
flush();
|
||
}}>SBS只输出带位置的飞机</Checkbox>
|
||
}
|
||
{
|
||
(this.type != "SBS") &&
|
||
<>
|
||
<Select
|
||
prefix={<Prefix label="Mode-S类型"/>}
|
||
defaultValue=""
|
||
style={{
|
||
width: '100%'
|
||
}}
|
||
value={this.mode_s_output_type}
|
||
onChange={(value: string) => {
|
||
this.mode_s_output_type = value;
|
||
flush();
|
||
}}
|
||
options={[
|
||
{value: 'ALL_Mode_S', label: '全部的mode_s'},
|
||
{value: 'DF_11_17_18', label: 'DF_11_17_18'},
|
||
{value: 'NO_POS_Mode_S', label: '无位置的mode_s'},
|
||
]}
|
||
/>
|
||
<Checkbox checked={this.use_mode_ac} onChange={(e: CheckboxChangeEvent) => {
|
||
this.use_mode_ac = e.target.checked;
|
||
flush();
|
||
}}>Mode_AC</Checkbox>
|
||
</>
|
||
}
|
||
</>);
|
||
}
|
||
}
|
||
|
||
export class Data_Feed extends enhance.Base {
|
||
key: string = "未命名";
|
||
enable: boolean = false;
|
||
type: string = "";
|
||
output_format: Output_Format = new Output_Format();
|
||
state: State_Shower = new State_Shower(`${baseURL}/get_data_feed_state`, () => this.key);
|
||
|
||
onClick(that) {
|
||
|
||
}
|
||
|
||
center_with_add(): React.JSX.Element {
|
||
return <></>
|
||
}
|
||
|
||
center(): React.JSX.Element {
|
||
return <></>
|
||
}
|
||
|
||
render(props: any) {
|
||
return (
|
||
<div style={{
|
||
display: "flex",
|
||
flexDirection: "row",
|
||
justifyContent: "space-between", // 将子元素水平分散
|
||
alignItems: "center", // 子元素垂直居中
|
||
gap: "8px", // 设置元素之间的间距
|
||
width: "100%" // 确保容器宽度占满父容器
|
||
}}>
|
||
|
||
<Button type="dashed">
|
||
<Space size={6}>
|
||
<Tag color="blue" style={{ marginInlineEnd: 0 }}>{this.type}</Tag>
|
||
<Text type="secondary">:</Text>
|
||
<Text>{this.key}</Text>
|
||
</Space>
|
||
</Button>
|
||
<Button
|
||
icon={<SettingOutlined/>}
|
||
onClick={() => {
|
||
this.onClick(this);
|
||
}}>
|
||
{"格式: " + this.output_format.type}
|
||
</Button>
|
||
{
|
||
this.center_with_add()
|
||
}
|
||
{
|
||
this.center()
|
||
}
|
||
<div style={{
|
||
display: "flex",
|
||
flexDirection: "row",
|
||
alignItems: "center",
|
||
gap: "8px",
|
||
}}>
|
||
<this.state.x/>
|
||
<Switch_Bool that={this} field={"enable"}></Switch_Bool>
|
||
<Button type="primary" autoInsertSpace onClick={() => {
|
||
axios.post(`${baseURL}/update_data_feed`, this).then((res) => {
|
||
Object.assign(this, res.data);
|
||
});
|
||
message.success(`${this.type}类型,数据馈送源${this.key}设置成功!` );
|
||
}}>
|
||
确定
|
||
</Button>
|
||
</div>
|
||
|
||
</div>
|
||
);
|
||
}
|
||
}
|
||
|
||
class Data_Feed_Server extends Data_Feed {
|
||
port: number = 0;
|
||
dataSource: any[] = [];
|
||
|
||
// 新增:轮询定时器
|
||
private pollTimer: number | null = null;
|
||
private pollIntervalMs = 1000;
|
||
|
||
private fetchConnections = async () => {
|
||
try {
|
||
// 你原来这里用 post 并把 this 作为 body,我保留这个行为
|
||
const res = await axios.post(`${baseURL}/get_data_feed_server_connect`, this);
|
||
this.dataSource = (res.data ?? []).map((item: any, index: number) => ({
|
||
...item,
|
||
key: String(index),
|
||
}));
|
||
this.flush();
|
||
} catch (e) {
|
||
// 可选:失败不打断轮询
|
||
// console.error(e);
|
||
}
|
||
};
|
||
|
||
private startPolling = () => {
|
||
if (this.pollTimer !== null) return; // 已经在轮询
|
||
void this.fetchConnections(); // 先立刻拉一次
|
||
this.pollTimer = window.setInterval(() => {
|
||
void this.fetchConnections();
|
||
}, this.pollIntervalMs);
|
||
};
|
||
|
||
private stopPolling = () => {
|
||
if (this.pollTimer === null) return;
|
||
window.clearInterval(this.pollTimer);
|
||
this.pollTimer = null;
|
||
};
|
||
|
||
refresh() {
|
||
// refresh 保持可用:手动刷新一次
|
||
void this.fetchConnections();
|
||
}
|
||
|
||
center(): React.JSX.Element {
|
||
return (
|
||
<>
|
||
<Select
|
||
prefix={<Prefix label="已连接客户端" />}
|
||
popupMatchSelectWidth={false}
|
||
placeholder="展示所有连接"
|
||
onChange={(value) => {
|
||
console.log("你选择了:", value);
|
||
}}
|
||
value={""}
|
||
onOpenChange={(open) => {
|
||
if (open) {
|
||
this.startPolling(); // 展开:1 秒刷新
|
||
} else {
|
||
this.stopPolling(); // 收起:停止刷新
|
||
}
|
||
}}
|
||
>
|
||
{this.dataSource.map((item: any) => (
|
||
<Option key={item.key} value={item.key}>
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
|
||
{Object.entries(item)
|
||
.filter(([k]) => k !== "key")
|
||
.map(([k, v]) => (
|
||
<div
|
||
key={k}
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "flex-start",
|
||
gap: 8,
|
||
lineHeight: 1.4,
|
||
}}
|
||
>
|
||
<strong style={{ whiteSpace: "nowrap" }}>{k}:</strong>
|
||
<span style={{ wordBreak: "break-all" }}>
|
||
{typeof v === "object" ? JSON.stringify(v) : String(v)}
|
||
</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</Option>
|
||
))}
|
||
</Select>
|
||
</>
|
||
);
|
||
}
|
||
|
||
center_with_add(): React.JSX.Element {
|
||
return (
|
||
<>
|
||
<Input_Port_Number that={this} field={"port"} />
|
||
</>
|
||
);
|
||
}
|
||
|
||
// 可选:如果这个对象有生命周期/销毁点,记得调用,避免内存泄漏
|
||
destroy() {
|
||
this.stopPolling();
|
||
}
|
||
}
|
||
|
||
class Data_Feed_Client extends Data_Feed {
|
||
url: string = "";
|
||
port: number = 0;
|
||
|
||
center_with_add() {
|
||
return <>
|
||
<Input_String that={this} field={"url"} name="IP地址"/>
|
||
<Input_Port_Number that={this} field={"port"}/>
|
||
</>
|
||
}
|
||
}
|
||
|
||
export class Data_Feed_TCP_Server extends Data_Feed_Server {
|
||
|
||
}
|
||
|
||
export class Data_Feed_UDP_Server extends Data_Feed_Server {
|
||
|
||
}
|
||
|
||
export class Data_Feed_TCP_Client extends Data_Feed_Client {
|
||
|
||
}
|
||
|
||
export class Data_Feed_UDP_Client extends Data_Feed_Client {
|
||
|
||
}
|
||
|
||
export function create_data_feed_from_type(type: string) {
|
||
let ret: Data_Feed
|
||
if (type === "Data_Feed_TCP_Server") {
|
||
ret = new Data_Feed_TCP_Server();
|
||
}
|
||
if (type === "Data_Feed_TCP_Client") {
|
||
ret = new Data_Feed_TCP_Client();
|
||
}
|
||
if (type === "Data_Feed_UDP_Server") {
|
||
ret = new Data_Feed_UDP_Server();
|
||
}
|
||
if (type === "Data_Feed_UDP_Client") {
|
||
ret = new Data_Feed_UDP_Client();
|
||
}
|
||
return ret;
|
||
}
|