wzyyy 3 gadi atpakaļ
vecāks
revīzija
f9023dddcb

+ 339 - 0
src/pages/device/Instance/Detail/Modbus/index.tsx

@@ -0,0 +1,339 @@
+import PermissionButton from '@/components/PermissionButton';
+import { Badge, Card, Empty, message, Tabs } from 'antd';
+import { useEffect, useRef, useState } from 'react';
+import { useIntl } from 'umi';
+import styles from '@/pages/link/Channel/Opcua/Access/index.less';
+import ProTable, { ActionType, ProColumns } from '@jetlinks/pro-table';
+import {
+  DeleteOutlined,
+  EditOutlined,
+  PlayCircleOutlined,
+  PlusOutlined,
+  StopOutlined,
+} from '@ant-design/icons';
+import { service } from '@/pages/link/Channel/Modbus';
+import Save from '@/pages/link/Channel/Modbus/Save';
+import { InstanceModel } from '@/pages/device/Instance';
+import AddPoint from '@/pages/link/Channel/Opcua/Access/addPoint';
+
+const Modbus = () => {
+  const intl = useIntl();
+  const { permission } = PermissionButton.usePermission('link/Channel/Modbus');
+  const [bindList, setBindList] = useState<any>([]);
+  const [opcId, setOpcId] = useState<string>('');
+  const actionRef = useRef<ActionType>();
+  const [param, setParam] = useState({});
+  const [visible, setVisible] = useState<boolean>(false);
+  const [channel, setChannel] = useState<any>({});
+  const [pointVisiable, setPointVisiable] = useState<boolean>(false);
+  const [current, setCurrent] = useState<any>({});
+  const [deviceId, setDeviceId] = useState<any>('');
+
+  const columns: ProColumns<any>[] = [
+    {
+      title: '属性ID',
+      dataIndex: 'metadataId',
+    },
+    {
+      title: '功能码',
+      render: (record: any) => <>{record.function?.text}</>,
+    },
+    {
+      title: '读取起始位置',
+      render: (record: any) => <>{record.codecConfig?.readIndex}</>,
+    },
+    {
+      title: '读取长度',
+      render: (record: any) => <>{record.codecConfig?.readLength}</>,
+    },
+    {
+      title: '值',
+      // dataIndex: '4',
+      //   render: (record: any) => <>{propertyValue[record.property]}</>,
+    },
+    {
+      title: '状态',
+      dataIndex: 'state',
+      renderText: (state) => (
+        <Badge text={state?.text} status={state?.value === 'disabled' ? 'error' : 'success'} />
+      ),
+    },
+    {
+      title: '操作',
+      valueType: 'option',
+      align: 'center',
+      width: 200,
+      render: (text, record) => [
+        <PermissionButton
+          isPermission={permission.update}
+          key="edit"
+          onClick={() => {
+            setPointVisiable(true);
+            setCurrent(record);
+          }}
+          type={'link'}
+          style={{ padding: 0 }}
+          tooltip={{
+            title: intl.formatMessage({
+              id: 'pages.data.option.edit',
+              defaultMessage: '编辑',
+            }),
+          }}
+        >
+          <EditOutlined />
+        </PermissionButton>,
+        <PermissionButton
+          type="link"
+          key={'action'}
+          style={{ padding: 0 }}
+          popConfirm={{
+            title: intl.formatMessage({
+              id: `pages.data.option.${
+                record.state.value !== 'disabled' ? 'disabled' : 'enabled'
+              }.tips`,
+              defaultMessage: '确认禁用?',
+            }),
+            onConfirm: async () => {
+              const item = {
+                ...record,
+                state: record.state.value === 'enabled' ? 'disabled' : 'enabled',
+              };
+              await service.saveMetadataConfig(opcId, deviceId, item);
+              message.success(
+                intl.formatMessage({
+                  id: 'pages.data.option.success',
+                  defaultMessage: '操作成功!',
+                }),
+              );
+              actionRef.current?.reload();
+            },
+          }}
+          isPermission={permission.action}
+          tooltip={{
+            title: intl.formatMessage({
+              id: `pages.data.option.${record.state.value !== 'disabled' ? 'disabled' : 'enabled'}`,
+              defaultMessage: record.state.value !== 'disabled' ? '禁用' : '启用',
+            }),
+          }}
+        >
+          {record.state.value !== 'disabled' ? <StopOutlined /> : <PlayCircleOutlined />}
+        </PermissionButton>,
+        <PermissionButton
+          isPermission={permission.delete}
+          style={{ padding: 0 }}
+          disabled={record.state.value === 'enabled'}
+          tooltip={{
+            title:
+              record.state.value === 'disabled'
+                ? intl.formatMessage({
+                    id: 'pages.data.option.remove',
+                    defaultMessage: '删除',
+                  })
+                : '请先禁用该组件,再删除。',
+          }}
+          popConfirm={{
+            title: '确认删除',
+            onConfirm: async () => {
+              const resp: any = await service.removeMetadataConfig(record.id);
+              if (resp.status === 200) {
+                message.success(
+                  intl.formatMessage({
+                    id: 'pages.data.option.success',
+                    defaultMessage: '操作成功!',
+                  }),
+                );
+                actionRef.current?.reload();
+              }
+            },
+          }}
+          key="delete"
+          type="link"
+        >
+          <DeleteOutlined />
+        </PermissionButton>,
+      ],
+    },
+  ];
+
+  const getModbus = () => {
+    service
+      .query({
+        paging: false,
+      })
+      .then((res) => {
+        setBindList(res.result.data);
+        setOpcId(res.result?.[0]?.id);
+        // setParam({
+        //     sorts: [{ name: 'createTime', order: 'desc' }],
+        // })
+      });
+  };
+
+  useEffect(() => {
+    const { id } = InstanceModel.detail;
+    setDeviceId(id);
+    if (id) {
+      getModbus();
+    }
+  }, [visible]);
+
+  // useEffect(() => {
+  //     if(opcId){
+  //         setLoading(false)
+  //     }
+  //  }, [opcId])
+
+  return (
+    <Card>
+      <PermissionButton
+        onClick={() => {
+          setVisible(true);
+          setChannel({});
+        }}
+        isPermission={permission.add}
+        key="add"
+        icon={<PlusOutlined />}
+        type="dashed"
+        style={{ width: '200px', marginLeft: 20, marginBottom: 5 }}
+      >
+        新增通道
+      </PermissionButton>
+      {bindList.length > 0 ? (
+        <Tabs
+          tabPosition={'left'}
+          defaultActiveKey={opcId}
+          onChange={(e) => {
+            setOpcId(e);
+            setParam({
+              terms: [{ column: 'opcUaId', value: e }],
+            });
+          }}
+        >
+          {bindList.map((item: any) => (
+            <Tabs.TabPane
+              key={item.id}
+              tab={
+                <div className={styles.left}>
+                  <div style={{ width: '100px', textAlign: 'left' }}>{item.name}</div>
+                  <PermissionButton
+                    isPermission={permission.update}
+                    key="edit"
+                    onClick={() => {
+                      setVisible(true);
+                      setChannel(item);
+                    }}
+                    type={'link'}
+                    style={{ padding: 0 }}
+                    tooltip={{
+                      title: intl.formatMessage({
+                        id: 'pages.data.option.edit',
+                        defaultMessage: '编辑',
+                      }),
+                    }}
+                  >
+                    <EditOutlined />
+                  </PermissionButton>
+                  <PermissionButton
+                    isPermission={permission.delete}
+                    style={{ padding: 0 }}
+                    popConfirm={{
+                      title: '确认删除',
+                      onConfirm: async () => {
+                        const resp: any = await service.remove(item.id);
+                        if (resp.status === 200) {
+                          getModbus();
+                          message.success(
+                            intl.formatMessage({
+                              id: 'pages.data.option.success',
+                              defaultMessage: '操作成功!',
+                            }),
+                          );
+                        }
+                      },
+                    }}
+                    key="delete"
+                    type="link"
+                  >
+                    <DeleteOutlined />
+                  </PermissionButton>
+                </div>
+              }
+            >
+              <ProTable
+                actionRef={actionRef}
+                // loading={loading}
+                params={param}
+                columns={columns}
+                rowKey="id"
+                search={false}
+                headerTitle={
+                  <>
+                    <PermissionButton
+                      onClick={() => {
+                        setPointVisiable(true);
+                        setCurrent({});
+                      }}
+                      isPermission={permission.add}
+                      key="add"
+                      icon={<PlusOutlined />}
+                      type="primary"
+                    >
+                      {intl.formatMessage({
+                        id: 'pages.data.option.add',
+                        defaultMessage: '新增',
+                      })}
+                    </PermissionButton>
+                  </>
+                }
+                request={async (params) => {
+                  console.log(opcId);
+                  // setTimeout(() => {
+                  //     const master =
+                  // }, 10);
+                  const res = await service.queryMetadataConfig(opcId, deviceId, {
+                    ...params,
+                    sorts: [{ name: 'createTime', order: 'desc' }],
+                  });
+                  // setData(res.result.data);
+                  return {
+                    code: res.message,
+                    result: {
+                      data: res.result.data,
+                      pageIndex: 0,
+                      pageSize: 0,
+                      total: 0,
+                    },
+                    status: res.status,
+                  };
+                }}
+              />
+            </Tabs.TabPane>
+          ))}
+        </Tabs>
+      ) : (
+        <Empty />
+      )}
+      {visible && (
+        <Save
+          data={channel}
+          close={() => {
+            setVisible(false);
+          }}
+          device={InstanceModel.detail}
+        />
+      )}
+      {pointVisiable && (
+        <AddPoint
+          deviceId={deviceId}
+          opcUaId={opcId}
+          data={current}
+          close={() => {
+            setPointVisiable(false);
+            actionRef.current?.reload();
+          }}
+        />
+      )}
+    </Card>
+  );
+};
+export default Modbus;

+ 332 - 0
src/pages/device/Instance/Detail/Opcua/index.tsx

@@ -0,0 +1,332 @@
+import PermissionButton from '@/components/PermissionButton';
+import { Badge, Card, Empty, message, Tabs } from 'antd';
+import { useEffect, useRef, useState } from 'react';
+import { useIntl } from 'umi';
+import styles from '@/pages/link/Channel/Opcua/Access/index.less';
+import ProTable, { ActionType, ProColumns } from '@jetlinks/pro-table';
+import {
+  DeleteOutlined,
+  EditOutlined,
+  PlayCircleOutlined,
+  PlusOutlined,
+  StopOutlined,
+} from '@ant-design/icons';
+import { service } from '@/pages/link/Channel/Opcua';
+import Save from '@/pages/link/Channel/Opcua/Save';
+import { InstanceModel } from '@/pages/device/Instance';
+import AddPoint from '@/pages/link/Channel/Opcua/Access/addPoint';
+
+const Opcua = () => {
+  const intl = useIntl();
+  const { permission } = PermissionButton.usePermission('link/Channel/Opcua');
+  const [bindList, setBindList] = useState<any>([]);
+  const [opcId, setOpcId] = useState<string>('');
+  const actionRef = useRef<ActionType>();
+  const [param, setParam] = useState({});
+  const [visible, setVisible] = useState<boolean>(false);
+  const [channel, setChannel] = useState<any>({});
+  const [pointVisiable, setPointVisiable] = useState<boolean>(false);
+  const [current, setCurrent] = useState<any>({});
+  const [deviceId, setDeviceId] = useState<any>('');
+
+  const columns: ProColumns<any>[] = [
+    {
+      title: '属性ID',
+      dataIndex: 'property',
+    },
+    {
+      title: '名称',
+      dataIndex: 'name',
+    },
+    {
+      title: 'OPC点位ID',
+      dataIndex: 'opcPointId',
+    },
+    {
+      title: '数据类型',
+      dataIndex: 'dataType',
+    },
+    {
+      title: '值',
+      // dataIndex: '4',
+      //   render: (record: any) => <>{propertyValue[record.property]}</>,
+    },
+    {
+      title: '状态',
+      dataIndex: 'state',
+      renderText: (state) => (
+        <Badge text={state?.text} status={state?.value === 'disable' ? 'error' : 'success'} />
+      ),
+    },
+    {
+      title: '操作',
+      valueType: 'option',
+      align: 'center',
+      width: 200,
+      render: (text, record) => [
+        <PermissionButton
+          isPermission={permission.update}
+          key="edit"
+          onClick={() => {
+            setPointVisiable(true);
+            setCurrent(record);
+          }}
+          type={'link'}
+          style={{ padding: 0 }}
+          tooltip={{
+            title: intl.formatMessage({
+              id: 'pages.data.option.edit',
+              defaultMessage: '编辑',
+            }),
+          }}
+        >
+          <EditOutlined />
+        </PermissionButton>,
+        <PermissionButton
+          type="link"
+          key={'action'}
+          style={{ padding: 0 }}
+          popConfirm={{
+            title: intl.formatMessage({
+              id: `pages.data.option.${record.state.value !== 'disable' ? 'disable' : 'good'}.tips`,
+              defaultMessage: '确认禁用?',
+            }),
+            onConfirm: async () => {
+              if (record.state.value === 'disable') {
+                await service.enablePoint(record.deviceId, [record.id]);
+              } else {
+                await service.stopPoint(record.deviceId, [record.id]);
+              }
+              message.success(
+                intl.formatMessage({
+                  id: 'pages.data.option.success',
+                  defaultMessage: '操作成功!',
+                }),
+              );
+              actionRef.current?.reload();
+            },
+          }}
+          isPermission={permission.action}
+          tooltip={{
+            title: intl.formatMessage({
+              id: `pages.data.option.${record.state.value !== 'disable' ? 'disable' : 'good'}`,
+              defaultMessage: record.state.value !== 'disable' ? '禁用' : '启用',
+            }),
+          }}
+        >
+          {record.state.value !== 'disable' ? <StopOutlined /> : <PlayCircleOutlined />}
+        </PermissionButton>,
+        <PermissionButton
+          isPermission={permission.delete}
+          style={{ padding: 0 }}
+          disabled={record.state.value === 'good'}
+          tooltip={{
+            title:
+              record.state.value === 'disable'
+                ? intl.formatMessage({
+                    id: 'pages.data.option.remove',
+                    defaultMessage: '删除',
+                  })
+                : '请先禁用该组件,再删除。',
+          }}
+          popConfirm={{
+            title: '确认删除',
+            onConfirm: async () => {
+              const resp: any = await service.deletePoint(record.id);
+              if (resp.status === 200) {
+                message.success(
+                  intl.formatMessage({
+                    id: 'pages.data.option.success',
+                    defaultMessage: '操作成功!',
+                  }),
+                );
+                actionRef.current?.reload();
+              }
+            },
+          }}
+          key="delete"
+          type="link"
+        >
+          <DeleteOutlined />
+        </PermissionButton>,
+      ],
+    },
+  ];
+
+  const getOpc = (id: string) => {
+    service
+      .noPagingOpcua({
+        paging: false,
+        terms: [
+          {
+            column: 'id$bind-opc',
+            value: id,
+          },
+        ],
+      })
+      .then((res) => {
+        setBindList(res.result);
+        setOpcId(res.result?.[0]?.id);
+        setParam({
+          terms: [{ column: 'opcUaId', value: res.result?.[0]?.id }],
+        });
+      });
+  };
+
+  useEffect(() => {
+    const { id } = InstanceModel.detail;
+    setDeviceId(id);
+    if (id) {
+      getOpc(id);
+    }
+  }, [visible]);
+
+  return (
+    <Card>
+      <PermissionButton
+        onClick={() => {
+          setVisible(true);
+          setChannel({});
+        }}
+        isPermission={permission.add}
+        key="add"
+        icon={<PlusOutlined />}
+        type="dashed"
+        style={{ width: '200px', marginLeft: 20, marginBottom: 5 }}
+      >
+        新增通道
+      </PermissionButton>
+      {bindList.length > 0 ? (
+        <Tabs
+          tabPosition={'left'}
+          defaultActiveKey={opcId}
+          onChange={(e) => {
+            setOpcId(e);
+            setParam({
+              terms: [{ column: 'opcUaId', value: e }],
+            });
+          }}
+        >
+          {bindList.map((item: any) => (
+            <Tabs.TabPane
+              key={item.id}
+              tab={
+                <div className={styles.left}>
+                  <div style={{ width: '100px', textAlign: 'left' }}>{item.name}</div>
+                  <PermissionButton
+                    isPermission={permission.update}
+                    key="edit"
+                    onClick={() => {
+                      setVisible(true);
+                      setChannel(item);
+                    }}
+                    type={'link'}
+                    style={{ padding: 0 }}
+                    tooltip={{
+                      title: intl.formatMessage({
+                        id: 'pages.data.option.edit',
+                        defaultMessage: '编辑',
+                      }),
+                    }}
+                  >
+                    <EditOutlined />
+                  </PermissionButton>
+                  <PermissionButton
+                    isPermission={permission.delete}
+                    style={{ padding: 0 }}
+                    popConfirm={{
+                      title: '确认删除',
+                      onConfirm: async () => {
+                        const resp: any = await service.remove(item.id);
+                        if (resp.status === 200) {
+                          getOpc(deviceId);
+                          message.success(
+                            intl.formatMessage({
+                              id: 'pages.data.option.success',
+                              defaultMessage: '操作成功!',
+                            }),
+                          );
+                        }
+                      },
+                    }}
+                    key="delete"
+                    type="link"
+                  >
+                    <DeleteOutlined />
+                  </PermissionButton>
+                </div>
+              }
+            >
+              <ProTable
+                actionRef={actionRef}
+                params={param}
+                columns={columns}
+                rowKey="id"
+                search={false}
+                headerTitle={
+                  <>
+                    <PermissionButton
+                      onClick={() => {
+                        setPointVisiable(true);
+                        setCurrent({});
+                      }}
+                      isPermission={permission.add}
+                      key="add"
+                      icon={<PlusOutlined />}
+                      type="primary"
+                    >
+                      {intl.formatMessage({
+                        id: 'pages.data.option.add',
+                        defaultMessage: '新增',
+                      })}
+                    </PermissionButton>
+                  </>
+                }
+                request={async (params) => {
+                  const res = await service.PointList({
+                    ...params,
+                    sorts: [{ name: 'createTime', order: 'desc' }],
+                  });
+                  // setData(res.result.data);
+                  return {
+                    code: res.message,
+                    result: {
+                      data: res.result.data,
+                      pageIndex: 0,
+                      pageSize: 0,
+                      total: 0,
+                    },
+                    status: res.status,
+                  };
+                }}
+              />
+            </Tabs.TabPane>
+          ))}
+        </Tabs>
+      ) : (
+        <Empty />
+      )}
+      {visible && (
+        <Save
+          data={channel}
+          close={() => {
+            setVisible(false);
+          }}
+          device={InstanceModel.detail}
+        />
+      )}
+      {pointVisiable && (
+        <AddPoint
+          deviceId={deviceId}
+          opcUaId={opcId}
+          data={current}
+          close={() => {
+            setPointVisiable(false);
+            actionRef.current?.reload();
+          }}
+        />
+      )}
+    </Card>
+  );
+};
+export default Opcua;

+ 12 - 0
src/pages/device/Instance/Detail/index.tsx

@@ -13,6 +13,8 @@ import Running from '@/pages/device/Instance/Detail/Running';
 import ChildDevice from '@/pages/device/Instance/Detail/ChildDevice';
 import ChildDevice from '@/pages/device/Instance/Detail/ChildDevice';
 import Diagnose from '@/pages/device/Instance/Detail/Diagnose';
 import Diagnose from '@/pages/device/Instance/Detail/Diagnose';
 import MetadataMap from '@/pages/device/Instance/Detail/MetadataMap';
 import MetadataMap from '@/pages/device/Instance/Detail/MetadataMap';
+import Opcua from '@/pages/device/Instance/Detail/Opcua';
+import Modbus from '@/pages/device/Instance/Detail/Modbus';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import Metadata from '../../components/Metadata';
 import Metadata from '../../components/Metadata';
 import type { DeviceMetadata } from '@/pages/device/Product/typings';
 import type { DeviceMetadata } from '@/pages/device/Product/typings';
@@ -147,6 +149,16 @@ const InstanceDetail = observer(() => {
       tab: '物模型映射',
       tab: '物模型映射',
       component: <MetadataMap type="device" />,
       component: <MetadataMap type="device" />,
     },
     },
+    {
+      key: 'opcua',
+      tab: 'OPC UA',
+      component: <Opcua />,
+    },
+    {
+      key: 'modbus',
+      tab: 'Modbus',
+      component: <Modbus />,
+    },
   ];
   ];
   const [list, setList] =
   const [list, setList] =
     useState<{ key: string; tab: string | ReactNode; component: ReactNode }[]>(baseList);
     useState<{ key: string; tab: string | ReactNode; component: ReactNode }[]>(baseList);

+ 1 - 0
src/pages/link/Channel/Modbus/Save/index.tsx

@@ -10,6 +10,7 @@ import { message } from 'antd';
 interface Props {
 interface Props {
   data: any;
   data: any;
   close: () => void;
   close: () => void;
+  device?: any;
 }
 }
 
 
 const Save = (props: Props) => {
 const Save = (props: Props) => {

+ 1 - 1
src/pages/link/Channel/Opcua/Access/addPoint/index.tsx

@@ -17,7 +17,7 @@ const AddPoint = (props: Props) => {
 
 
   const handleSave = async () => {
   const handleSave = async () => {
     const formData = await form.validateFields();
     const formData = await form.validateFields();
-    const { name } = property.find((item: any) => item.id === formData.property);
+    const { name } = property?.find((item: any) => item.id === formData.property);
     if (props.data.id) {
     if (props.data.id) {
       if (formData.enableCalculate) {
       if (formData.enableCalculate) {
         service
         service

+ 0 - 1
src/pages/link/Channel/Opcua/Access/bindDevice/index.tsx

@@ -60,7 +60,6 @@ const BindDevice = (props: Props) => {
       productId: item.productId,
       productId: item.productId,
       productName: item.productName,
       productName: item.productName,
     }));
     }));
-    console.log(params);
     service.bind(params).then((res) => {
     service.bind(params).then((res) => {
       if (res.status === 200) {
       if (res.status === 200) {
         message.success('绑定成功');
         message.success('绑定成功');

+ 27 - 7
src/pages/link/Channel/Opcua/Save/index.tsx

@@ -11,6 +11,7 @@ import { useEffect, useState } from 'react';
 interface Props {
 interface Props {
   data: Partial<OpaUa>;
   data: Partial<OpaUa>;
   close: () => void;
   close: () => void;
+  device?: any;
 }
 }
 
 
 const Save = (props: Props) => {
 const Save = (props: Props) => {
@@ -219,19 +220,38 @@ const Save = (props: Props) => {
         }
         }
       });
       });
     } else {
     } else {
-      service.save(item).then((res: any) => {
-        if (res.status === 200) {
-          message.success('保存成功');
-          props.close();
-        }
-      });
+      if (props.device) {
+        service.save(item).then((res: any) => {
+          if (res.status === 200) {
+            const params = {
+              opcUaId: res.result.id,
+              deviceId: props.device.id,
+              deviceName: props.device.name,
+              productId: props.device.productId,
+              productName: props.device.productName,
+            };
+            service.bind(params).then((resp) => {
+              if (resp.status === 200) {
+                message.success('保存成功');
+                props.close();
+              }
+            });
+          }
+        });
+      } else {
+        service.save(item).then((res: any) => {
+          if (res.status === 200) {
+            message.success('保存成功');
+            props.close();
+          }
+        });
+      }
     }
     }
   };
   };
 
 
   useEffect(() => {
   useEffect(() => {
     service.policies().then((res) => setPolicies(res.result));
     service.policies().then((res) => setPolicies(res.result));
     service.modes().then((res) => setModes(res.result));
     service.modes().then((res) => setModes(res.result));
-    console.log(props.data.clientConfigs?.[0]);
   }, []);
   }, []);
   return (
   return (
     <Modal
     <Modal

+ 5 - 0
src/pages/link/Channel/Opcua/service.ts

@@ -75,6 +75,11 @@ class Service extends BaseService<OpaUa> {
       method: 'POST',
       method: 'POST',
       data,
       data,
     });
     });
+  noPagingOpcua = (data: any) =>
+    request(`/${SystemConst.API_BASE}/opc/client/_query/no-paging`, {
+      method: 'POST',
+      data,
+    });
 }
 }
 
 
 export default Service;
 export default Service;