import React, { useEffect, useLayoutEffect, useState } from "react"; import { Link, useParams, useNavigate } from 'react-router-dom'; import ListTalkgroups from "../Talkgroups/ListTalkgroups.js"; import MessageBox from "../Components/Message.js"; import ListGroups from "../Group/ListGroups.js"; import GroupModal from "../Group/GroupModal.js"; import ErrorChart from "./ResponsiveErrorChart" import CallChart from "./ResponsiveCallChart" import { useGetSystemsQuery, useGetTalkgroupsQuery, useGetGroupsQuery, useGetErrorsQuery, useDeleteGroupMutation, useDeleteSystemMutation, useImportTalkgroupsMutation, useSaveGroupOrderMutation } from '../features/api/apiSlice' import { Button, Confirm, Icon, Form, TextArea, Message, Header, Container, List, Segment, Tab, Divider } from "semantic-ui-react"; const System = () => { const { shortName } = useParams(); const navigate = useNavigate(); const { data: systemsData, isSuccess: isSystemsSuccess } = useGetSystemsQuery(); const { data: talkgroupsData, isSuccess: isTalkgroupsSuccess } = useGetTalkgroupsQuery(shortName); const { data: groupsData } = useGetGroupsQuery(shortName); //const { data: systemErrorData, isSuccess: isErrorsSuccess } = useGetErrorsQuery(shortName); const [deleteGroupAPI] = useDeleteGroupMutation(); const [reorderGroupsAPI] = useSaveGroupOrderMutation(); const [importTalkgroupsAPI, { error: importError }] = useImportTalkgroupsMutation(); const [deleteSystemAPI ] = useDeleteSystemMutation(); const [openSystemDeleteConfirm, setOpenSystemDeleteConfirm] = useState(false); const [openMessage, setOpenMessage] = useState(false); const [requestMessage, setRequestMessage] = useState(""); const [groupVisible, setGroupVisible] = useState(false); const [editGroupId, setEditGroupId] = useState(false); const [errorData, setErrorData] = useState({}); const [callStatistics, setCallStatistics] = useState({}); const [groupOrder, setGroupOrder] = useState([]); const processErrors = (statistics) => { const errors = statistics.decodeErrorsFreq; var allData = []; var num = 0; const now = new Date(); var maxDate = now; var minDate = now; var minValue = 0; var maxValue = 0; var legend = []; var MS_PER_MINUTE = 60000; for (const freq in errors) { const freqErrors = errors[freq]; var data = []; for (var i = 0; i < freqErrors.errorHistory.length; i++) { let spotsBack = i; //freqErrors.errorHistory.length - i; let time = new Date(now - spotsBack * 15 * MS_PER_MINUTE); if (time < minDate) minDate = time; if (freqErrors.errorHistory[i] > maxValue) maxValue = freqErrors.errorHistory[i]; var value = { id: num++, y: freqErrors.errorHistory[i], x: time }; data.push(value); } const id = Math.floor(freq / 1000) / 1000 + "MHz"; legend.push(id); var obj = { id: id, data: data }; allData.push(obj); } const errorData = { data: allData, minDate: minDate, maxDate: maxDate, minValue: minValue, maxValue: maxValue, legend: legend }; setErrorData(errorData); }; const processStatistics = (statistic) => { var callTotals = []; var uploadErrors= []; let talkgroupStats = []; let decodeErrorsFreq = []; const now = new Date(); var maxDate = now; var minDate = now; var minValue = 0; var maxValue = 0; var MS_PER_MINUTE = 60000; if (statistic) { for (let j = 0; j < statistic.callTotals.length; j++) { let spotsBack = j;//statistic.callTotals.length - j; let time = new Date(now - spotsBack * 15 * MS_PER_MINUTE); if (time < minDate) minDate = time; if (statistic.callTotals[j] > maxValue) maxValue = statistic.callTotals[j]; callTotals.push({ y: statistic.callTotals[j], x: time }); } for (let j = 0; j < statistic.uploadErrors.length; j++) { let spotsBack = statistic.uploadErrors.length - j; let time = new Date(now - spotsBack * 15 * MS_PER_MINUTE); if (time < minDate) minDate = time; if (statistic.uploadErrors[j] > maxValue) maxValue = statistic.uploadErrors[j]; uploadErrors.push({ x: time, y: statistic.uploadErrors[j] }); } const data = { minDate: minDate, maxDate: maxDate, minValue: minValue, maxValue: maxValue, callTotals: callTotals, uploadErrors: uploadErrors }; setCallStatistics(data); } } /* componentDidMount() { this.props.systemActions.fetchSystems(); this.props.talkgroupActions.fetchTalkgroups(this.props.shortName); this.props.groupActions.fetchGroups(this.props.shortName); this.props.errorActions.fetch(this.props.shortName); if (this.props.statistic) { this.processStatistics(this.props.statistic); } if (this.props.errors) { this.processErrors(this.props.errors); } } componentDidUpdate(prevProp) { // If we just received the error statistics for a system, for the first time, proccess them. if (this.props.errors && !prevProp.errors) { this.processErrors(this.props.errors); } // If we just received the call statistics for a system, for the first time, proccess them. if (this.props.statistic && !prevProp.statistic) { this.processStatistics(this.props.statistic); } } */ const editGroup = (groupId) => { setEditGroupId(groupId) setGroupVisible((state) => !state) } const deleteGroup = async (groupId) => { for (const num in groupsData) { if (groupsData[num]._id === groupId) { await deleteGroupAPI(groupsData[num]) } } } const reorderGroup = (oldIndex, newIndex) => { if ((newIndex < 0) || (newIndex > (groupOrder.length - 1))) { return } const moveOrder = groupOrder.slice(); moveOrder.splice(newIndex, 0, moveOrder.splice(oldIndex, 1)[0]); setGroupOrder(moveOrder); } const handleGroupClose = () => { setEditGroupId(false) setGroupVisible(false) } const handleGroupToggle = () => { setEditGroupId(false) setGroupVisible((state) => !state) } const saveGroupOrder = async () => { await reorderGroupsAPI({ shortName: shortName, order: { groupOrder: JSON.stringify(groupOrder) } }); } const handleUpload = async (file) => { const formData = new FormData(); formData.append('file', file) await importTalkgroupsAPI({ shortName: shortName, file: formData }); } const handleSystemDeleteConfirm = async () => { setOpenSystemDeleteConfirm(false); try { await deleteSystemAPI(shortName).unwrap(); navigate("/list-systems") } catch (error) { const message = error.data.message; console.log(message); setRequestMessage(message); } }; const handleSystemDeleteCancel = () => setOpenSystemDeleteConfirm(false); let system = false; if (isSystemsSuccess) { system = systemsData.systems.find(sys => sys.shortName === shortName); } useLayoutEffect(() => { if (groupsData) { let newOrder = []; groupsData.forEach(group => { newOrder.push(group._id); }); setGroupOrder(newOrder); } }, [groupsData]) useEffect(() => { if (isSystemsSuccess && systemsData.stats[shortName]) { processStatistics(systemsData.stats[shortName]) processErrors(systemsData.stats[shortName]) } }, [isSystemsSuccess, systemsData]); /* useEffect(() => { if (isErrorsSuccess) { processErrors(systemErrorData) } }, [systemErrorData]);*/ let fileInput = null; var location = ""; var example = ""; var groupRender = () => { }; if (system) { if (isTalkgroupsSuccess && talkgroupsData.length) { groupRender = () => (
); } else { groupRender = () => ( ); } example = '{\n\t"systems": [\n\t\t{\n\t\t\t"shortName": "' + system.shortName + '",\n\t\t\t"apiKey": "' + system.key + '"\n\t\t}\n\t],\n\t"uploadServer": "' + process.env.REACT_APP_BACKEND_SERVER + '"\n}'; switch (system.systemType) { case "state": location = system.state; break; default: case "city": location = system.city + ", " + system.state; break; case "county": location = system.county + ", " + system.state; break; case "international": location = system.country; break; } } const panes = [ { menuItem: "Overview", render: () => (
Location
{location}
Short Name
{system.shortName}
Description
{system.description}
{system.status &&
Status Message
{system.status}
}
) }, { menuItem: "Config", render: () => (
Configure Trunk Recorder

Below are the parameters you will need to configure Trunk Recorder. The example below shows how they should be added to the config.json file.

API Key
{system.key}
Short Name
{system.shortName}
Upload Server
{process.env.REACT_APP_BACKEND_SERVER}
Example