import {
Badge,
Button,
Input,
Popconfirm,
Popover,
Space,
Table,
Tooltip,
Typography,
notification,
} from "antd";
import { useTranslation } from "react-i18next";
import { useEffect, useRef, useState } from "react";
import {
Constants,
FormatDatetime,
hasOnePermission,
hasPermission,
myFetch,
myFetchContentType,
} from "../../../utils";
import MyPagination from "../../../Components/MyPagination";
import { Link } from "react-router-dom";
import {
FileTextOutlined,
LoadingOutlined,
PlusOutlined,
} from "@ant-design/icons";
import { MyCopyIcon, MyShowHiddenIcon } from "../../../Components/MyIcon";
import { useAppContext } from "../../../Contexts/AppContext";
const ReceivedSSECommands = {
UpdateRobotStatus: 1,
AddUnauthorizedRobot: 2,
AddRobot: 3,
RemoveUnauthorizedRobot: 4,
RemoveRobot: 5,
RobotUpdated: 6,
UpdateRobotCurrentJob: 7,
UpdateRobotJobsWaitingCount: 8,
PermitJoinUpdated: 9,
};
function getRobotTypeString(type) {
switch (type) {
case 1:
return "Rex";
case 2:
return "Yeet";
default:
return "Unknown";
}
}
export default function Robots() {
const { t } = useTranslation();
const appContext = useAppContext();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
const [robots, setRobots] = useState([]);
const [robotsTotalPages, setRobotsTotalPages] = useState(0);
const [robotsPaginationPage, setRobotsPaginationPage] = useState(1);
const robotsPaginationPageRef = useRef(1);
const [unauthorizedRobots, setUnauthorizedRobots] = useState([]);
const [unauthorizedRobotsTotalPages, setUnauthorizedRobotsTotalPages] =
useState(0);
const [
unauthorizedRobotsPaginationPage,
setUnauthorizedRobotsPaginationPage,
] = useState(1);
const unauthorizedRobotsPaginationPageRef = useRef(1);
const [selectedRobotName, setSelectedRobotName] = useState("");
const [permitJoinEnabled, setPermitJoinEnabled] = useState(false);
const [showAddress, setShowAddress] = useState(false);
const sseEventSource = useRef(null);
const getRobotStatusBadge = (status) => {
switch (status) {
case 1:
return (
);
case 2:
return (
);
case 3:
return (
);
case 4:
return (
);
case 5:
return (
);
default:
return "Unknown";
}
};
const getRobotsTableContent = () => {
let items = [
{
title: t("robotics.robots.column.id"),
dataIndex: "id",
key: "id",
},
{
title: t("robotics.robots.column.type"),
dataIndex: "type",
key: "type",
},
{
title: t("robotics.robots.column.name"),
dataIndex: "name",
key: "name",
},
{
title: t("robotics.robots.column.status"),
dataIndex: "status",
key: "status",
},
{
title: t("robotics.robots.column.currentJob"),
dataIndex: "currentJobName",
key: "currentJobName",
},
{
title: t("robotics.robots.column.jobsWaiting"),
dataIndex: "jobsWaitingCount",
key: "jobsWaitingCount",
render: (parameter, record) => (
<>
{parameter > 0 ? (
{record._jobsWaitingNameList.map((jobName, index) => (
{jobName}
))}
)
}
>
{parameter}
) : (
parameter
)}
>
),
},
];
if (
hasPermission(
appContext.userPermissions,
Constants.PERMISSIONS.ROBOTICS.ROBOTS.VIEW_ROBOTS_ADDRESSES
)
) {
items.push({
title: t("robotics.robots.column.address"),
dataIndex: "address",
key: "address",
render: (text) => (
{showAddress ? text : "XXXXXX"}
),
});
}
items.push(
{
title: t("robotics.robots.column.connectedAt"),
dataIndex: "connectedAt",
key: "connectedAt",
},
{
title: t("robotics.robots.column.firmwareVersion"),
dataIndex: "firmwareVersion",
key: "firmwareVersion",
},
{
title: t("robotics.robots.column.createdAt"),
dataIndex: "createdAt",
key: "createdAt",
}
);
if (
hasOnePermission(
appContext.userPermissions,
Constants.PERMISSIONS.ROBOTICS.ROBOTS.EDIT_ROBOT_NAME,
Constants.PERMISSIONS.ROBOTICS.ROBOTS.DISCONNECT_ROBOT
)
) {
items.push({
title: t("robotics.robots.column.actions"),
dataIndex: "actions",
key: "actions",
render: (_, record) => (
{hasPermission(
appContext.userPermissions,
Constants.PERMISSIONS.ROBOTICS.ROBOTS.EDIT_ROBOT_NAME
) && (
setSelectedRobotName(e.target.value)}
maxLength={Constants.GLOBALS.MAX_ROBOTICS_ROBOT_NAME_LENGTH}
/>
}
okButtonProps={{
disabled:
selectedRobotName.length <
Constants.GLOBALS.MIN_ROBOTICS_ROBOT_NAME_LENGTH ||
selectedRobotName.length >
Constants.GLOBALS.MAX_ROBOTICS_ROBOT_NAME_LENGTH,
}}
okText={t("common.button.confirm")}
cancelText={t("common.button.cancel")}
onConfirm={() =>
myFetch(
`/robot`,
"PATCH",
{
robotId: record.id,
robotName: selectedRobotName,
},
{},
myFetchContentType.JSON,
Constants.ROBOTICS_API_ADDRESS
).catch((err) => {
if (err === 422) {
notificationApi["error"]({
message: t(
"robotics.robots.popconfirmEdit.errorNotification.message"
),
description: t(
"robotics.robots.popconfirmEdit.errorNotification.description"
),
});
}
})
}
>
setSelectedRobotName(record.name)}>
{t("common.text.edit")}
)}
{hasPermission(
appContext.userPermissions,
Constants.PERMISSIONS.ROBOTICS.ROBOTS.DISCONNECT_ROBOT
) && (
myFetch(
`/robot/${record.id}`,
"DELETE",
null,
{},
myFetchContentType.JSON,
Constants.ROBOTICS_API_ADDRESS
)
}
>
{t("common.text.disconnect")}
)}
),
});
}
return items;
};
const getRobotsTableItems = (robots) => {
let items = [];
robots.sort((a, b) => a.Status - b.Status);
robots.forEach((robot) =>
items.push({
key: robot.Id,
id: robot.Id,
type: getRobotTypeString(robot.Type),
name: robot.Name,
status: getRobotStatusBadge(robot.Status),
currentJobName:
robot.CurrentJobName === ""
? Constants.TEXT_EMPTY_PLACEHOLDER
: robot.CurrentJobName,
jobsWaitingCount: robot.JobsWaitingCount,
_jobsWaitingNameList: robot.JobsWaitingNameList,
address: robot.Address,
firmwareVersion: robot.FirmwareVersion,
connectedAt: FormatDatetime(robot.ConnectedAt),
createdAt: FormatDatetime(robot.CreatedAt),
actions: robot.Actions,
})
);
return items;
};
const getUnauthorizedTableContent = () => {
let items = [
{
title: t("robotics.robots.column.id"),
dataIndex: "id",
key: "id",
},
{
title: t("robotics.robots.column.type"),
dataIndex: "type",
key: "type",
},
{
title: t("robotics.robots.column.address"),
dataIndex: "address",
key: "address",
},
{
title: t("robotics.robots.column.connectedAt"),
dataIndex: "connectedAt",
key: "connectedAt",
},
{
title: t("robotics.robots.column.firmwareVersion"),
dataIndex: "firmwareVersion",
key: "firmwareVersion",
},
];
if (
hasPermission(
appContext.userPermissions,
Constants.PERMISSIONS.ROBOTICS.ROBOTS.AUTHORIZE_DENY_UNAUTHORIZED_ROBOTS
)
) {
items.push({
title: t("robotics.robots.column.actions"),
dataIndex: "actions",
key: "actions",
render: (_, record) => (
myFetch(
`/robot/deny/${record.id}`,
"DELETE",
null,
{},
myFetchContentType.JSON,
Constants.ROBOTICS_API_ADDRESS
)
}
>
{t("common.text.deny")}
myFetch(
`/robot/authorize/${record.id}`,
"POST",
null,
{},
myFetchContentType.JSON,
Constants.ROBOTICS_API_ADDRESS
)
}
>
{t("common.text.authorize")}
),
});
}
return items;
};
const getUnauthorizedTableItems = (unauthorizedRobots) => {
let items = [];
unauthorizedRobots.forEach((robot) => {
items.push({
key: robot.Id,
id: robot.Id,
type: getRobotTypeString(robot.Type),
address: robot.Address,
connectedAt: FormatDatetime(robot.ConnectedAt),
firmwareVersion: robot.FirmwareVersion,
actions: robot.Actions,
});
});
return items;
};
// type = 0 => robots, type = 1 => unauthorizedRobots
const fetchRobots = (type, page = 1) => {
myFetch(
`/${type === 1 ? "u" : ""}robots?page=${page}`,
"GET",
null,
{},
myFetchContentType.JSON,
Constants.ROBOTICS_API_ADDRESS
).then((data) => {
if (type === 1) {
setUnauthorizedRobots(
data.UnauthorizedRobots === null ? [] : data.UnauthorizedRobots
);
setUnauthorizedRobotsTotalPages(data.TotalPages);
} else {
setRobots(data.Robots === null ? [] : data.Robots);
setRobotsTotalPages(data.TotalPages);
}
});
};
useEffect(() => fetchRobots(0, robotsPaginationPage), [robotsPaginationPage]);
useEffect(
() => fetchRobots(1, unauthorizedRobotsPaginationPage),
[unauthorizedRobotsPaginationPage]
);
useEffect(() => {
myFetch(
"/permitjoin",
"GET",
null,
{},
myFetchContentType.JSON,
Constants.ROBOTICS_API_ADDRESS
).then((data) => setPermitJoinEnabled(data.Enabled));
sseEventSource.current = new EventSource(
`${Constants.ROBOTICS_API_ADDRESS}/sse`
);
sseEventSource.current.onmessage = (event) => {
const data = JSON.parse(event.data);
const cmd = data.Cmd;
const body = data.Body;
console.log("sse message", data);
switch (cmd) {
case ReceivedSSECommands.UpdateRobotStatus:
setRobots((arr) => {
const newArr = [...arr];
const index = arr.findIndex((x) => x.Id === body.RobotId);
if (index !== -1) {
newArr[index].Status = body.Status;
}
return newArr;
});
break;
case ReceivedSSECommands.AddUnauthorizedRobot:
console.log("a", unauthorizedRobotsPaginationPageRef.current);
if (unauthorizedRobotsPaginationPageRef.current === 1) {
setUnauthorizedRobots((arr) => {
const newArr = [...arr];
const index = arr.findIndex(
(x) => x.Id === body.UnauthorizedRobot.Id
);
if (index !== -1) {
newArr[index] = body.UnauthorizedRobot;
} else {
if (
newArr.length ===
Constants.GLOBALS.ROBOTICS_UNAUTHORIZED_PAGINATION_LIMIT
) {
newArr.pop();
}
newArr.unshift(body.UnauthorizedRobot);
}
return newArr;
});
}
setUnauthorizedRobotsTotalPages(body.TotalPages);
break;
case ReceivedSSECommands.AddRobot:
if (robotsPaginationPageRef.current === 1) {
setRobots((arr) => {
const newArr = [...arr];
const index = arr.findIndex((x) => x.Id === body.Robot.Id);
if (index !== -1) {
newArr[index] = body.Robot;
} else {
if (
newArr.length ===
Constants.GLOBALS.ROBOTICS_ROBOTS_PAGINATION_LIMIT
) {
newArr.pop();
}
newArr.unshift(body.Robot);
}
return newArr;
});
}
setRobotsTotalPages(body.TotalPages);
// remove from unauthorized robots
setUnauthorizedRobots((arr) => {
const newArr = [...arr];
const index = arr.findIndex((x) => x.Id === body.Robot.Id);
if (index !== -1) {
newArr.splice(index, 1);
}
return newArr;
});
setUnauthorizedRobotsTotalPages(body.UnauthorizedRobotsTotalPages);
// if user is on the last page and the last item is removed, we need to go back one page
if (
body.UnauthorizedRobotsTotalPages > 0 &&
unauthorizedRobotsPaginationPageRef.current >
body.UnauthorizedRobotsTotalPages
) {
unauthorizedRobotsPaginationPageRef.current--;
setUnauthorizedRobotsPaginationPage(
unauthorizedRobotsPaginationPageRef.current
);
}
break;
case ReceivedSSECommands.RemoveUnauthorizedRobot:
setUnauthorizedRobots((arr) => {
const newArr = [...arr];
const index = arr.findIndex(
(x) => x.Id === body.UnauthorizedRobotId
);
if (index !== -1) {
newArr.splice(index, 1);
}
return newArr;
});
setUnauthorizedRobotsTotalPages(body.TotalPages);
// if user is on the last page and the last item is removed, we need to go back one page
if (
body.TotalPages > 0 &&
unauthorizedRobotsPaginationPageRef.current > body.TotalPages
) {
unauthorizedRobotsPaginationPageRef.current--;
setUnauthorizedRobotsPaginationPage(
unauthorizedRobotsPaginationPageRef.current
);
}
break;
case ReceivedSSECommands.RemoveRobot:
setRobots((arr) => {
const newArr = [...arr];
const index = arr.findIndex((x) => x.Id === body.RobotId);
if (index !== -1) {
newArr.splice(index, 1);
}
return newArr;
});
setRobotsTotalPages(body.TotalPages);
// if user is on the last page and the last item is removed, we need to go back one page
if (
body.TotalPages > 0 &&
robotsPaginationPageRef.current > body.TotalPages
) {
robotsPaginationPageRef.current--;
setRobotsPaginationPage(robotsPaginationPageRef.current);
}
break;
case ReceivedSSECommands.RobotUpdated:
setRobots((arr) => {
const newArr = [...arr];
const index = arr.findIndex((x) => x.Id === body.RobotId);
if (index !== -1) {
newArr[index].Name = body.RobotName;
}
return newArr;
});
break;
case ReceivedSSECommands.UpdateRobotCurrentJob:
setRobots((arr) => {
const newArr = [...arr];
const index = arr.findIndex((x) => x.Id === body.RobotId);
if (index !== -1) {
newArr[index].CurrentJobName = body.JobName;
newArr[index].JobsWaitingNameList = body.JobsWaitingNameList;
}
return newArr;
});
break;
case ReceivedSSECommands.UpdateRobotJobsWaitingCount:
setRobots((arr) => {
const newArr = [...arr];
const index = arr.findIndex((x) => x.Id === body.RobotId);
if (index !== -1) {
newArr[index].JobsWaitingCount = body.JobsWaitingCount;
newArr[index].JobsWaitingNameList = body.JobsWaitingNameList;
}
return newArr;
});
break;
case ReceivedSSECommands.PermitJoinUpdated:
setPermitJoinEnabled(body);
break;
default:
break;
}
};
sseEventSource.current.onerror = (event) => console.log("sse error", event);
sseEventSource.current.onopen = (event) => console.log("sse open", event);
sseEventSource.current.onclose = (event) => console.log("sse close", event);
return () => sseEventSource.current.close();
}, []);
return (
<>
{notificationContextHolder}
{t("robotics.robots.header")}{" "}
{
setRobotsPaginationPage(page);
robotsPaginationPageRef.current = page;
}}
totalPages={robotsTotalPages}
/>
{t("robotics.unauthorizedRobots.header")}
{
setUnauthorizedRobotsPaginationPage(page);
unauthorizedRobotsPaginationPageRef.current = page;
}}
totalPages={unauthorizedRobotsTotalPages}
/>
>
);
}