mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-06-01 23:40:45 +00:00
Set device tags after FUOTA complete.
This commit is contained in:
parent
bbdf2dd781
commit
98ba2f3198
3
api/proto/api/fuota.proto
vendored
3
api/proto/api/fuota.proto
vendored
@ -156,6 +156,9 @@ message FuotaDeployment {
|
|||||||
// Payload.
|
// Payload.
|
||||||
// The FUOTA payload to send.
|
// The FUOTA payload to send.
|
||||||
bytes payload = 21;
|
bytes payload = 21;
|
||||||
|
|
||||||
|
// Set device tags on complete.
|
||||||
|
map<string, string> on_complete_set_device_tags = 22;
|
||||||
}
|
}
|
||||||
|
|
||||||
message FuotaDeploymentListItem {
|
message FuotaDeploymentListItem {
|
||||||
|
3
api/rust/proto/chirpstack/api/fuota.proto
vendored
3
api/rust/proto/chirpstack/api/fuota.proto
vendored
@ -156,6 +156,9 @@ message FuotaDeployment {
|
|||||||
// Payload.
|
// Payload.
|
||||||
// The FUOTA payload to send.
|
// The FUOTA payload to send.
|
||||||
bytes payload = 21;
|
bytes payload = 21;
|
||||||
|
|
||||||
|
// Set device tags on complete.
|
||||||
|
map<string, string> on_complete_set_device_tags = 22;
|
||||||
}
|
}
|
||||||
|
|
||||||
message FuotaDeploymentListItem {
|
message FuotaDeploymentListItem {
|
||||||
|
@ -23,7 +23,8 @@ create table fuota_deployment (
|
|||||||
fragmentation_block_ack_delay smallint not null,
|
fragmentation_block_ack_delay smallint not null,
|
||||||
fragmentation_descriptor bytea not null,
|
fragmentation_descriptor bytea not null,
|
||||||
request_fragmentation_session_status varchar(20) not null,
|
request_fragmentation_session_status varchar(20) not null,
|
||||||
payload bytea not null
|
payload bytea not null,
|
||||||
|
on_complete_set_device_tags jsonb not null
|
||||||
);
|
);
|
||||||
|
|
||||||
create table fuota_deployment_device (
|
create table fuota_deployment_device (
|
||||||
|
@ -23,7 +23,8 @@ create table fuota_deployment (
|
|||||||
fragmentation_block_ack_delay smallint not null,
|
fragmentation_block_ack_delay smallint not null,
|
||||||
fragmentation_descriptor blob not null,
|
fragmentation_descriptor blob not null,
|
||||||
request_fragmentation_session_status varchar(20) not null,
|
request_fragmentation_session_status varchar(20) not null,
|
||||||
payload blob not null
|
payload blob not null,
|
||||||
|
on_complete_set_device_tags text not null
|
||||||
);
|
);
|
||||||
|
|
||||||
create table fuota_deployment_device (
|
create table fuota_deployment_device (
|
||||||
|
@ -77,6 +77,9 @@ impl FuotaService for Fuota {
|
|||||||
.request_fragmentation_session_status()
|
.request_fragmentation_session_status()
|
||||||
.from_proto(),
|
.from_proto(),
|
||||||
payload: req_dp.payload.clone(),
|
payload: req_dp.payload.clone(),
|
||||||
|
on_complete_set_device_tags: fields::KeyValue::new(
|
||||||
|
req_dp.on_complete_set_device_tags.clone(),
|
||||||
|
),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
if req_dp.calculate_fragmentation_fragment_size {
|
if req_dp.calculate_fragmentation_fragment_size {
|
||||||
@ -152,6 +155,7 @@ impl FuotaService for Fuota {
|
|||||||
payload: dp.payload.clone(),
|
payload: dp.payload.clone(),
|
||||||
calculate_multicast_timeout: false,
|
calculate_multicast_timeout: false,
|
||||||
calculate_fragmentation_fragment_size: false,
|
calculate_fragmentation_fragment_size: false,
|
||||||
|
on_complete_set_device_tags: dp.on_complete_set_device_tags.into_hashmap(),
|
||||||
}),
|
}),
|
||||||
created_at: Some(helpers::datetime_to_prost_timestamp(&dp.created_at)),
|
created_at: Some(helpers::datetime_to_prost_timestamp(&dp.created_at)),
|
||||||
updated_at: Some(helpers::datetime_to_prost_timestamp(&dp.updated_at)),
|
updated_at: Some(helpers::datetime_to_prost_timestamp(&dp.updated_at)),
|
||||||
@ -227,6 +231,9 @@ impl FuotaService for Fuota {
|
|||||||
.request_fragmentation_session_status()
|
.request_fragmentation_session_status()
|
||||||
.from_proto(),
|
.from_proto(),
|
||||||
payload: req_dp.payload.clone(),
|
payload: req_dp.payload.clone(),
|
||||||
|
on_complete_set_device_tags: fields::KeyValue::new(
|
||||||
|
req_dp.on_complete_set_device_tags.clone(),
|
||||||
|
),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
if req_dp.calculate_fragmentation_fragment_size {
|
if req_dp.calculate_fragmentation_fragment_size {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use std::ops::DerefMut;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
@ -11,7 +12,7 @@ use crate::config;
|
|||||||
use crate::downlink;
|
use crate::downlink;
|
||||||
use crate::gpstime::ToGpsTime;
|
use crate::gpstime::ToGpsTime;
|
||||||
use crate::storage::fields::{FuotaJob, RequestFragmentationSessionStatus};
|
use crate::storage::fields::{FuotaJob, RequestFragmentationSessionStatus};
|
||||||
use crate::storage::{device_keys, device_profile, device_queue, fuota, multicast};
|
use crate::storage::{device, device_keys, device_profile, device_queue, fuota, multicast};
|
||||||
|
|
||||||
pub struct Flow {
|
pub struct Flow {
|
||||||
scheduler_interval: Duration,
|
scheduler_interval: Duration,
|
||||||
@ -569,7 +570,7 @@ impl Flow {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Complete FUOTA deployment");
|
info!("Completing FUOTA deployment");
|
||||||
self.job.attempt_count += 1;
|
self.job.attempt_count += 1;
|
||||||
|
|
||||||
if self.fuota_deployment.request_fragmentation_session_status
|
if self.fuota_deployment.request_fragmentation_session_status
|
||||||
@ -590,6 +591,20 @@ impl Flow {
|
|||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let fuota_devices = fuota::get_devices(self.job.fuota_deployment_id.into(), -1, 0).await?;
|
||||||
|
let fuota_devices: Vec<fuota::FuotaDeploymentDevice> = fuota_devices
|
||||||
|
.into_iter()
|
||||||
|
.filter(|d| d.completed_at.is_some() && d.error_msg.is_empty())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for fuota_device in &fuota_devices {
|
||||||
|
let mut d = device::get(&fuota_device.dev_eui).await?;
|
||||||
|
for (k, v) in self.fuota_deployment.on_complete_set_device_tags.iter() {
|
||||||
|
d.tags.deref_mut().insert(k.to_string(), v.to_string());
|
||||||
|
}
|
||||||
|
let _ = device::update(d).await?;
|
||||||
|
}
|
||||||
|
|
||||||
let mut d = self.fuota_deployment.clone();
|
let mut d = self.fuota_deployment.clone();
|
||||||
d.completed_at = Some(Utc::now());
|
d.completed_at = Some(Utc::now());
|
||||||
let _ = fuota::update_deployment(d).await?;
|
let _ = fuota::update_deployment(d).await?;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use chrono::{DateTime, Duration, Utc};
|
use chrono::{DateTime, Duration, Utc};
|
||||||
use diesel::{dsl, prelude::*};
|
use diesel::{dsl, prelude::*};
|
||||||
@ -43,6 +45,7 @@ pub struct FuotaDeployment {
|
|||||||
pub fragmentation_descriptor: Vec<u8>,
|
pub fragmentation_descriptor: Vec<u8>,
|
||||||
pub request_fragmentation_session_status: fields::RequestFragmentationSessionStatus,
|
pub request_fragmentation_session_status: fields::RequestFragmentationSessionStatus,
|
||||||
pub payload: Vec<u8>,
|
pub payload: Vec<u8>,
|
||||||
|
pub on_complete_set_device_tags: fields::KeyValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FuotaDeployment {
|
impl Default for FuotaDeployment {
|
||||||
@ -76,6 +79,7 @@ impl Default for FuotaDeployment {
|
|||||||
request_fragmentation_session_status:
|
request_fragmentation_session_status:
|
||||||
fields::RequestFragmentationSessionStatus::NoRequest,
|
fields::RequestFragmentationSessionStatus::NoRequest,
|
||||||
payload: Vec::new(),
|
payload: Vec::new(),
|
||||||
|
on_complete_set_device_tags: fields::KeyValue::new(HashMap::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,6 +231,7 @@ pub async fn update_deployment(d: FuotaDeployment) -> Result<FuotaDeployment, Er
|
|||||||
fuota_deployment::request_fragmentation_session_status
|
fuota_deployment::request_fragmentation_session_status
|
||||||
.eq(&d.request_fragmentation_session_status),
|
.eq(&d.request_fragmentation_session_status),
|
||||||
fuota_deployment::payload.eq(&d.payload),
|
fuota_deployment::payload.eq(&d.payload),
|
||||||
|
fuota_deployment::on_complete_set_device_tags.eq(&d.on_complete_set_device_tags),
|
||||||
))
|
))
|
||||||
.get_result(&mut get_async_db_conn().await?)
|
.get_result(&mut get_async_db_conn().await?)
|
||||||
.await
|
.await
|
||||||
|
@ -213,6 +213,7 @@ diesel::table! {
|
|||||||
#[max_length = 20]
|
#[max_length = 20]
|
||||||
request_fragmentation_session_status -> Varchar,
|
request_fragmentation_session_status -> Varchar,
|
||||||
payload -> Bytea,
|
payload -> Bytea,
|
||||||
|
on_complete_set_device_tags -> Jsonb,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,6 +189,7 @@ diesel::table! {
|
|||||||
fragmentation_descriptor -> Binary,
|
fragmentation_descriptor -> Binary,
|
||||||
request_fragmentation_session_status -> Text,
|
request_fragmentation_session_status -> Text,
|
||||||
payload -> Binary,
|
payload -> Binary,
|
||||||
|
on_complete_set_device_tags -> Text,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { Form, Input, InputNumber, Select, Row, Col, Button, Upload, UploadFile, Switch } from "antd";
|
import { Tabs, Form, Input, InputNumber, Select, Row, Col, Button, Upload, UploadFile, Switch } from "antd";
|
||||||
import { UploadOutlined } from "@ant-design/icons";
|
import { UploadOutlined, MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
|
||||||
|
|
||||||
import type { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
import type { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
||||||
import { FuotaDeployment, RequestFragmentationSessionStatus } from "@chirpstack/chirpstack-api-grpc-web/api/fuota_pb";
|
import { FuotaDeployment, RequestFragmentationSessionStatus } from "@chirpstack/chirpstack-api-grpc-web/api/fuota_pb";
|
||||||
@ -79,6 +79,11 @@ function FuotaDeploymentForm(props: IProps) {
|
|||||||
d.setFragmentationFragmentSize(v.fragmentationFragmentSize);
|
d.setFragmentationFragmentSize(v.fragmentationFragmentSize);
|
||||||
d.setPayload(v.payload);
|
d.setPayload(v.payload);
|
||||||
|
|
||||||
|
// on complete set device tags
|
||||||
|
for (const elm of v.onCompleteSetDeviceTagsMap) {
|
||||||
|
d.getOnCompleteSetDeviceTagsMap().set(elm[0], elm[1]);
|
||||||
|
}
|
||||||
|
|
||||||
props.onFinish(d);
|
props.onFinish(d);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -152,206 +157,250 @@ function FuotaDeploymentForm(props: IProps) {
|
|||||||
onFinishFailed={onFinishFailed}
|
onFinishFailed={onFinishFailed}
|
||||||
form={form}
|
form={form}
|
||||||
>
|
>
|
||||||
<Form.Item label="Name" name="name" rules={[{ required: true, message: "Please enter a name!" }]}>
|
<Tabs>
|
||||||
<Input disabled={props.disabled} />
|
<Tabs.TabPane tab="Deployment" key="1">
|
||||||
</Form.Item>
|
<Form.Item label="Name" name="name" rules={[{ required: true, message: "Please enter a name!" }]}>
|
||||||
<Row gutter={24}>
|
<Input disabled={props.disabled} />
|
||||||
<Col span={16}>
|
|
||||||
<AutocompleteInput
|
|
||||||
label="Device profile"
|
|
||||||
name="deviceProfileId"
|
|
||||||
getOption={getDeviceProfileOption}
|
|
||||||
getOptions={getDeviceProfileOptions}
|
|
||||||
disabled={props.disabled || props.update}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col span={8}>
|
|
||||||
<Form.Item
|
|
||||||
label="Unicast retry count (max)"
|
|
||||||
name="unicastMaxRetryCount"
|
|
||||||
tooltip="This defines how many times ChirpStack will retry unicast commands in case not acknowledged by the end-device."
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<InputNumber min={0} max={5} disabled={props.disabled} style={{ width: "100%" }} />
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
<Row gutter={24}>
|
||||||
</Row>
|
<Col span={16}>
|
||||||
<Row gutter={24}>
|
<AutocompleteInput
|
||||||
<Col span={8}>
|
label="Device profile"
|
||||||
<Form.Item
|
name="deviceProfileId"
|
||||||
label="Multicast group-type"
|
getOption={getDeviceProfileOption}
|
||||||
name="multicastGroupType"
|
getOptions={getDeviceProfileOptions}
|
||||||
tooltip="The multicast-group type defines the way how multicast frames are scheduled by the network-server."
|
disabled={props.disabled || props.update}
|
||||||
rules={[{ required: true, message: "Please select a multicast group-type!" }]}
|
required
|
||||||
>
|
/>
|
||||||
<Select onChange={onMulticastGroupTypeChange} disabled={props.disabled}>
|
</Col>
|
||||||
<Select.Option value={MulticastGroupType.CLASS_C}>Class-C</Select.Option>
|
<Col span={8}>
|
||||||
<Select.Option value={MulticastGroupType.CLASS_B}>Class-B</Select.Option>
|
<Form.Item
|
||||||
</Select>
|
label="Unicast retry count (max)"
|
||||||
|
name="unicastMaxRetryCount"
|
||||||
|
tooltip="This defines how many times ChirpStack will retry unicast commands in case not acknowledged by the end-device."
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<InputNumber min={0} max={5} disabled={props.disabled} style={{ width: "100%" }} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={24}>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item
|
||||||
|
label="Multicast group-type"
|
||||||
|
name="multicastGroupType"
|
||||||
|
tooltip="The multicast-group type defines the way how multicast frames are scheduled by the network-server."
|
||||||
|
rules={[{ required: true, message: "Please select a multicast group-type!" }]}
|
||||||
|
>
|
||||||
|
<Select onChange={onMulticastGroupTypeChange} disabled={props.disabled}>
|
||||||
|
<Select.Option value={MulticastGroupType.CLASS_C}>Class-C</Select.Option>
|
||||||
|
<Select.Option value={MulticastGroupType.CLASS_B}>Class-B</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item label="Class-B ping-slot periodicity" name="multicastClassBPingSlotNbK">
|
||||||
|
<Select disabled={!isMulticastClassB || props.disabled}>
|
||||||
|
<Select.Option value={0}>Every second</Select.Option>
|
||||||
|
<Select.Option value={1}>Every 2 seconds</Select.Option>
|
||||||
|
<Select.Option value={2}>Every 4 seconds</Select.Option>
|
||||||
|
<Select.Option value={3}>Every 8 seconds</Select.Option>
|
||||||
|
<Select.Option value={4}>Every 16 seconds</Select.Option>
|
||||||
|
<Select.Option value={5}>Every 32 seconds</Select.Option>
|
||||||
|
<Select.Option value={6}>Every 64 seconds</Select.Option>
|
||||||
|
<Select.Option value={7}>Every 128 seconds</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item
|
||||||
|
label="Class-C scheduling type"
|
||||||
|
name="multicastClassCSchedulingType"
|
||||||
|
tooltip="In order to reach all devices, it might be needed to transmit a downlink through multiple gateways. In case of Delay each gateway will transmit one by one, in case of GPS Time all required gateways will transmit at the same GPS time."
|
||||||
|
>
|
||||||
|
<Select disabled={isMulticastClassB || props.disabled}>
|
||||||
|
<Select.Option value={MulticastGroupSchedulingType.DELAY}>Delay</Select.Option>
|
||||||
|
<Select.Option value={MulticastGroupSchedulingType.GPS_TIME}>GPS Time</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={24}>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item
|
||||||
|
label="Multicast data-rate"
|
||||||
|
name="multicastDr"
|
||||||
|
rules={[{ required: true, message: "Please enter a multicast data-rate!" }]}
|
||||||
|
tooltip="The data-rate to use when transmitting the multicast frames. Please refer to the LoRaWAN Regional Parameters specification for valid values."
|
||||||
|
>
|
||||||
|
<InputNumber min={0} max={15} disabled={props.disabled} style={{ width: "100%" }} addonBefore="DR" />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item
|
||||||
|
label="Multicast frequency (Hz)"
|
||||||
|
name="multicastFrequency"
|
||||||
|
tooltip="The frequency to use when transmitting the multicast frames. Please refer to the LoRaWAN Regional Parameters specification for valid values."
|
||||||
|
rules={[{ required: true, message: "Please enter a frequency!" }]}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} disabled={props.disabled} style={{ width: "100%" }} addonAfter="Hz" />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item label="Fragmentation redundancy (%)" name="fragmentationRedundancyPercentage">
|
||||||
|
<InputNumber min={0} max={100} addonAfter="%" style={{ width: "100%" }} disabled={props.disabled} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={24}>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item
|
||||||
|
label="Fragmentation status request"
|
||||||
|
name="requestFragmentationSessionStatus"
|
||||||
|
tooltip="After fragment enqueue is recommended for Class-A devices, after session timeout is recommended for Class-B / Class-C devices."
|
||||||
|
>
|
||||||
|
<Select disabled={props.disabled}>
|
||||||
|
<Select.Option value={RequestFragmentationSessionStatus.NO_REQUEST}>Do not request</Select.Option>
|
||||||
|
<Select.Option value={RequestFragmentationSessionStatus.AFTER_FRAGMENT_ENQUEUE}>
|
||||||
|
After fragment enqueue
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value={RequestFragmentationSessionStatus.AFTER_SESSION_TIMEOUT}>
|
||||||
|
After session timeout
|
||||||
|
</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={24}>
|
||||||
|
<Col span={6}>
|
||||||
|
<Form.Item
|
||||||
|
label="Calculate multicast-timeout"
|
||||||
|
name="calculateMulticastTimeout"
|
||||||
|
tooltip="If checked, ChirpStack will calculate the multicast-timeout."
|
||||||
|
>
|
||||||
|
<Switch onChange={(v: boolean) => setCalculateMulticastTimeout(v)} disabled={props.disabled} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={6}>
|
||||||
|
<Form.Item label="Multicast timeout" name="multicastTimeout">
|
||||||
|
{isMulticastClassB && (
|
||||||
|
<Select disabled={props.disabled || calculateMulticastTimeout}>
|
||||||
|
<Select.Option value={0}>1 beacon period</Select.Option>
|
||||||
|
<Select.Option value={1}>2 beacon periods</Select.Option>
|
||||||
|
<Select.Option value={2}>4 beacon periods</Select.Option>
|
||||||
|
<Select.Option value={3}>8 beacon periods</Select.Option>
|
||||||
|
<Select.Option value={4}>16 beacon periods</Select.Option>
|
||||||
|
<Select.Option value={5}>32 beacon periods</Select.Option>
|
||||||
|
<Select.Option value={6}>64 beacon periods</Select.Option>
|
||||||
|
<Select.Option value={7}>128 beacon periods</Select.Option>
|
||||||
|
<Select.Option value={8}>256 beacon periods</Select.Option>
|
||||||
|
<Select.Option value={9}>512 beacon periods</Select.Option>
|
||||||
|
<Select.Option value={10}>1024 beacon periods</Select.Option>
|
||||||
|
<Select.Option value={11}>2048 beacon periods</Select.Option>
|
||||||
|
<Select.Option value={12}>4096 beacon periods</Select.Option>
|
||||||
|
<Select.Option value={13}>8192 beacon periods</Select.Option>
|
||||||
|
<Select.Option value={14}>16384 beacon periods</Select.Option>
|
||||||
|
<Select.Option value={15}>32768 beacon periods</Select.Option>
|
||||||
|
</Select>
|
||||||
|
)}
|
||||||
|
{!isMulticastClassB && (
|
||||||
|
<Select disabled={props.disabled || calculateMulticastTimeout}>
|
||||||
|
<Select.Option value={0}>1 second</Select.Option>
|
||||||
|
<Select.Option value={1}>2 seconds</Select.Option>
|
||||||
|
<Select.Option value={2}>4 seconds</Select.Option>
|
||||||
|
<Select.Option value={3}>8 seconds</Select.Option>
|
||||||
|
<Select.Option value={4}>16 seconds</Select.Option>
|
||||||
|
<Select.Option value={5}>32 seconds</Select.Option>
|
||||||
|
<Select.Option value={6}>64 seconds</Select.Option>
|
||||||
|
<Select.Option value={7}>128 seconds</Select.Option>
|
||||||
|
<Select.Option value={8}>256 seconds</Select.Option>
|
||||||
|
<Select.Option value={9}>512 seconds</Select.Option>
|
||||||
|
<Select.Option value={10}>1024 seconds</Select.Option>
|
||||||
|
<Select.Option value={11}>2048 seconds</Select.Option>
|
||||||
|
<Select.Option value={12}>4096 seconds</Select.Option>
|
||||||
|
<Select.Option value={13}>8192 seconds</Select.Option>
|
||||||
|
<Select.Option value={14}>16384 seconds</Select.Option>
|
||||||
|
<Select.Option value={15}>32768 seconds</Select.Option>
|
||||||
|
</Select>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={6}>
|
||||||
|
<Form.Item
|
||||||
|
label="Calculate fragment size"
|
||||||
|
name="calculateFragmentationFragmentSize"
|
||||||
|
tooltip="If checked, ChirpStack will calculate the fragment size for fragmentation."
|
||||||
|
>
|
||||||
|
<Switch onChange={(v: boolean) => setCalculateFragmentationFragmentSize(v)} disabled={props.disabled} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={6}>
|
||||||
|
<Form.Item label="Fragment size" name="fragmentationFragmentSize">
|
||||||
|
<InputNumber
|
||||||
|
min={0}
|
||||||
|
max={255}
|
||||||
|
disabled={props.disabled || calculateFragmentationFragmentSize}
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
addonAfter="Bytes"
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Form.Item label="Payload" name="payload" required>
|
||||||
|
<Upload
|
||||||
|
beforeUpload={beforeUpload}
|
||||||
|
onRemove={onRemoveUpload}
|
||||||
|
maxCount={1}
|
||||||
|
fileList={fileList}
|
||||||
|
disabled={props.disabled}
|
||||||
|
>
|
||||||
|
<Button icon={<UploadOutlined />} disabled={props.disabled}>
|
||||||
|
Click to upload
|
||||||
|
</Button>
|
||||||
|
</Upload>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Tabs.TabPane>
|
||||||
<Col span={8}>
|
<Tabs.TabPane tab="Set device tags (on complete)" key="2">
|
||||||
<Form.Item label="Class-B ping-slot periodicity" name="multicastClassBPingSlotNbK">
|
<Form.List name="onCompleteSetDeviceTagsMap">
|
||||||
<Select disabled={!isMulticastClassB || props.disabled}>
|
{(fields, { add, remove }) => (
|
||||||
<Select.Option value={0}>Every second</Select.Option>
|
<>
|
||||||
<Select.Option value={1}>Every 2 seconds</Select.Option>
|
{fields.map(({ key, name, ...restField }) => (
|
||||||
<Select.Option value={2}>Every 4 seconds</Select.Option>
|
<Row gutter={24}>
|
||||||
<Select.Option value={3}>Every 8 seconds</Select.Option>
|
<Col span={6}>
|
||||||
<Select.Option value={4}>Every 16 seconds</Select.Option>
|
<Form.Item
|
||||||
<Select.Option value={5}>Every 32 seconds</Select.Option>
|
{...restField}
|
||||||
<Select.Option value={6}>Every 64 seconds</Select.Option>
|
name={[name, 0]}
|
||||||
<Select.Option value={7}>Every 128 seconds</Select.Option>
|
fieldKey={[name, 0]}
|
||||||
</Select>
|
rules={[{ required: true, message: "Please enter a key!" }]}
|
||||||
</Form.Item>
|
>
|
||||||
</Col>
|
<Input placeholder="Key" />
|
||||||
<Col span={8}>
|
</Form.Item>
|
||||||
<Form.Item
|
</Col>
|
||||||
label="Class-C scheduling type"
|
<Col span={16}>
|
||||||
name="multicastClassCSchedulingType"
|
<Form.Item
|
||||||
tooltip="In order to reach all devices, it might be needed to transmit a downlink through multiple gateways. In case of Delay each gateway will transmit one by one, in case of GPS Time all required gateways will transmit at the same GPS time."
|
{...restField}
|
||||||
>
|
name={[name, 1]}
|
||||||
<Select disabled={isMulticastClassB || props.disabled}>
|
fieldKey={[name, 1]}
|
||||||
<Select.Option value={MulticastGroupSchedulingType.DELAY}>Delay</Select.Option>
|
rules={[{ required: true, message: "Please enter a value!" }]}
|
||||||
<Select.Option value={MulticastGroupSchedulingType.GPS_TIME}>GPS Time</Select.Option>
|
>
|
||||||
</Select>
|
<Input placeholder="Value" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
<Col span={2}>
|
||||||
<Row gutter={24}>
|
<MinusCircleOutlined onClick={() => remove(name)} />
|
||||||
<Col span={8}>
|
</Col>
|
||||||
<Form.Item
|
</Row>
|
||||||
label="Multicast data-rate"
|
))}
|
||||||
name="multicastDr"
|
<Form.Item>
|
||||||
rules={[{ required: true, message: "Please enter a multicast data-rate!" }]}
|
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
|
||||||
tooltip="The data-rate to use when transmitting the multicast frames. Please refer to the LoRaWAN Regional Parameters specification for valid values."
|
Add tag
|
||||||
>
|
</Button>
|
||||||
<InputNumber min={0} max={15} disabled={props.disabled} style={{ width: "100%" }} addonBefore="DR" />
|
</Form.Item>
|
||||||
</Form.Item>
|
</>
|
||||||
</Col>
|
|
||||||
<Col span={8}>
|
|
||||||
<Form.Item
|
|
||||||
label="Multicast frequency (Hz)"
|
|
||||||
name="multicastFrequency"
|
|
||||||
tooltip="The frequency to use when transmitting the multicast frames. Please refer to the LoRaWAN Regional Parameters specification for valid values."
|
|
||||||
rules={[{ required: true, message: "Please enter a frequency!" }]}
|
|
||||||
>
|
|
||||||
<InputNumber min={0} disabled={props.disabled} style={{ width: "100%" }} addonAfter="Hz" />
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
<Col span={8}>
|
|
||||||
<Form.Item label="Fragmentation redundancy (%)" name="fragmentationRedundancyPercentage">
|
|
||||||
<InputNumber min={0} max={100} addonAfter="%" style={{ width: "100%" }} disabled={props.disabled} />
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row gutter={24}>
|
|
||||||
<Col span={8}>
|
|
||||||
<Form.Item
|
|
||||||
label="Fragmentation status request"
|
|
||||||
name="requestFragmentationSessionStatus"
|
|
||||||
tooltip="After fragment enqueue is recommended for Class-A devices, after session timeout is recommended for Class-B / Class-C devices."
|
|
||||||
>
|
|
||||||
<Select disabled={props.disabled}>
|
|
||||||
<Select.Option value={RequestFragmentationSessionStatus.NO_REQUEST}>Do not request</Select.Option>
|
|
||||||
<Select.Option value={RequestFragmentationSessionStatus.AFTER_FRAGMENT_ENQUEUE}>
|
|
||||||
After fragment enqueue
|
|
||||||
</Select.Option>
|
|
||||||
<Select.Option value={RequestFragmentationSessionStatus.AFTER_SESSION_TIMEOUT}>
|
|
||||||
After session timeout
|
|
||||||
</Select.Option>
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row gutter={24}>
|
|
||||||
<Col span={6}>
|
|
||||||
<Form.Item
|
|
||||||
label="Calculate multicast-timeout"
|
|
||||||
name="calculateMulticastTimeout"
|
|
||||||
tooltip="If checked, ChirpStack will calculate the multicast-timeout."
|
|
||||||
>
|
|
||||||
<Switch onChange={(v: boolean) => setCalculateMulticastTimeout(v)} disabled={props.disabled} />
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
<Col span={6}>
|
|
||||||
<Form.Item label="Multicast timeout" name="multicastTimeout">
|
|
||||||
{isMulticastClassB && (
|
|
||||||
<Select disabled={props.disabled || calculateMulticastTimeout}>
|
|
||||||
<Select.Option value={0}>1 beacon period</Select.Option>
|
|
||||||
<Select.Option value={1}>2 beacon periods</Select.Option>
|
|
||||||
<Select.Option value={2}>4 beacon periods</Select.Option>
|
|
||||||
<Select.Option value={3}>8 beacon periods</Select.Option>
|
|
||||||
<Select.Option value={4}>16 beacon periods</Select.Option>
|
|
||||||
<Select.Option value={5}>32 beacon periods</Select.Option>
|
|
||||||
<Select.Option value={6}>64 beacon periods</Select.Option>
|
|
||||||
<Select.Option value={7}>128 beacon periods</Select.Option>
|
|
||||||
<Select.Option value={8}>256 beacon periods</Select.Option>
|
|
||||||
<Select.Option value={9}>512 beacon periods</Select.Option>
|
|
||||||
<Select.Option value={10}>1024 beacon periods</Select.Option>
|
|
||||||
<Select.Option value={11}>2048 beacon periods</Select.Option>
|
|
||||||
<Select.Option value={12}>4096 beacon periods</Select.Option>
|
|
||||||
<Select.Option value={13}>8192 beacon periods</Select.Option>
|
|
||||||
<Select.Option value={14}>16384 beacon periods</Select.Option>
|
|
||||||
<Select.Option value={15}>32768 beacon periods</Select.Option>
|
|
||||||
</Select>
|
|
||||||
)}
|
)}
|
||||||
{!isMulticastClassB && (
|
</Form.List>
|
||||||
<Select disabled={props.disabled || calculateMulticastTimeout}>
|
</Tabs.TabPane>
|
||||||
<Select.Option value={0}>1 second</Select.Option>
|
</Tabs>
|
||||||
<Select.Option value={1}>2 seconds</Select.Option>
|
|
||||||
<Select.Option value={2}>4 seconds</Select.Option>
|
|
||||||
<Select.Option value={3}>8 seconds</Select.Option>
|
|
||||||
<Select.Option value={4}>16 seconds</Select.Option>
|
|
||||||
<Select.Option value={5}>32 seconds</Select.Option>
|
|
||||||
<Select.Option value={6}>64 seconds</Select.Option>
|
|
||||||
<Select.Option value={7}>128 seconds</Select.Option>
|
|
||||||
<Select.Option value={8}>256 seconds</Select.Option>
|
|
||||||
<Select.Option value={9}>512 seconds</Select.Option>
|
|
||||||
<Select.Option value={10}>1024 seconds</Select.Option>
|
|
||||||
<Select.Option value={11}>2048 seconds</Select.Option>
|
|
||||||
<Select.Option value={12}>4096 seconds</Select.Option>
|
|
||||||
<Select.Option value={13}>8192 seconds</Select.Option>
|
|
||||||
<Select.Option value={14}>16384 seconds</Select.Option>
|
|
||||||
<Select.Option value={15}>32768 seconds</Select.Option>
|
|
||||||
</Select>
|
|
||||||
)}
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
<Col span={6}>
|
|
||||||
<Form.Item
|
|
||||||
label="Calculate fragment size"
|
|
||||||
name="calculateFragmentationFragmentSize"
|
|
||||||
tooltip="If checked, ChirpStack will calculate the fragment size for fragmentation."
|
|
||||||
>
|
|
||||||
<Switch onChange={(v: boolean) => setCalculateFragmentationFragmentSize(v)} disabled={props.disabled} />
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
<Col span={6}>
|
|
||||||
<Form.Item label="Fragment size" name="fragmentationFragmentSize">
|
|
||||||
<InputNumber
|
|
||||||
min={0}
|
|
||||||
max={255}
|
|
||||||
disabled={props.disabled || calculateFragmentationFragmentSize}
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
addonAfter="Bytes"
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Form.Item label="Payload" name="payload" required>
|
|
||||||
<Upload
|
|
||||||
beforeUpload={beforeUpload}
|
|
||||||
onRemove={onRemoveUpload}
|
|
||||||
maxCount={1}
|
|
||||||
fileList={fileList}
|
|
||||||
disabled={props.disabled}
|
|
||||||
>
|
|
||||||
<Button icon={<UploadOutlined />} disabled={props.disabled}>
|
|
||||||
Click to upload
|
|
||||||
</Button>
|
|
||||||
</Upload>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Button type="primary" htmlType="submit" disabled={props.disabled}>
|
<Button type="primary" htmlType="submit" disabled={props.disabled}>
|
||||||
Submit
|
Submit
|
||||||
|
Loading…
x
Reference in New Issue
Block a user