Parcourir la source

feat: modbus/opc映射

Wzyyy98 il y a 3 ans
Parent
commit
6d0d4c561c

+ 8 - 4
src/pages/device/Instance/Detail/EdgeMap/mapTree/index.tsx

@@ -51,14 +51,14 @@ const MapTree = (props: Props) => {
   const save = async () => {
     // console.log(list,'list')
     const params: any[] = [];
-    const metadataId = metaData.map((item: any) => item.metadataId);
+    // const metadataId = metaData.map((item: any) => item.metadataId);
     list.forEach((item: any) => {
       const array = item.points.map((element: any) => ({
         channelId: item.parentId,
         collectorId: element.collectorId,
         pointId: element.id,
         metadataType: 'property',
-        metadataId: metadataId.find((i: any) => i === element.id),
+        metadataId: metaData.find((i: any) => i.name === element.name)?.metadataId,
         provider: data.find((it: any) => it.id === item.parentId).provider,
       }));
       params.push(...array);
@@ -97,7 +97,7 @@ const MapTree = (props: Props) => {
   useEffect(() => {
     service.treeMap(edgeId).then((res) => {
       if (res.status === 200) {
-        console.log(res.result?.[0], 'data');
+        // console.log(res.result?.[0], 'data');
         setData(res.result?.[0]);
         setExpandedKey([res.result?.[0].id]);
       }
@@ -116,7 +116,11 @@ const MapTree = (props: Props) => {
         close();
       }}
       onOk={() => {
-        save();
+        if (list && list.length !== 0) {
+          save();
+        } else {
+          onlyMessage('请选择采集器', 'warning');
+        }
       }}
       width="900px"
     >

+ 185 - 0
src/pages/device/Instance/Detail/MapChannel/Tree.tsx

@@ -0,0 +1,185 @@
+import { onlyMessage } from '@/utils/util';
+import { DeleteOutlined } from '@ant-design/icons';
+import { Button, Card, Modal, Tree, List, Popconfirm } from 'antd';
+import { useEffect, useRef, useState } from 'react';
+import { service } from '.';
+import './index.less';
+
+interface Props {
+  close: any;
+  deviceId: string;
+  metaData: any;
+  type: 'MODBUS_TCP' | 'OPC_UA';
+}
+
+const ChannelTree = (props: Props) => {
+  const { deviceId, close, metaData, type } = props;
+  const [data, setData] = useState<any>([]);
+  const [checked, setChecked] = useState<any>([]);
+  const filterRef = useRef<any>([]);
+  const [expandedKey, setExpandedKey] = useState<any>();
+  const [list, setList] = useState<any>([]);
+
+  const filterTree = (nodes: any[], lists: any[]) => {
+    if (!nodes?.length) {
+      return nodes;
+    }
+    return nodes.filter((item) => {
+      if (lists.indexOf(item.id) > -1) {
+        filterRef.current.push(item);
+        // console.log(filterRef.current, 'filterRef.current');
+        return false;
+      }
+      // 符合条件的保留,并且需要递归处理其子节点
+      item.collectors = filterTree(item.collectors, lists);
+      return true;
+    });
+  };
+
+  const pushTree = (node: any) => {
+    const newTree = data.map((item: any) => {
+      if (item.id === node.parentId) {
+        item.collectors.push(node);
+      }
+      return item;
+    });
+    setData(newTree);
+    filterRef.current = filterRef.current.filter((element: any) => element.id !== node.id);
+  };
+
+  const save = async () => {
+    console.log(metaData);
+    const params: any[] = [];
+    // const metadataName = metaData.map((item: any) => item.name);
+    list.forEach((item: any) => {
+      const array = item.points.map((element: any) => ({
+        channelId: item.parentId,
+        collectorId: element.collectorId,
+        pointId: element.id,
+        metadataType: 'property',
+        metadataId: metaData.find((i: any) => i.name === element.name)?.metadataId,
+        provider: data.find((it: any) => it.id === item.parentId).provider,
+      }));
+      params.push(...array);
+    });
+    const filterParms = params.filter((item) => !!item.metadataId);
+    // console.log(filterParms, params)
+    if (filterParms && filterParms.length !== 0) {
+      const res = await service.saveMap(deviceId, type, filterParms);
+      if (res.status === 200) {
+        onlyMessage('操作成功');
+        close();
+      }
+    } else {
+      onlyMessage('暂无对应属性的映射', 'warning');
+    }
+  };
+
+  useEffect(() => {
+    service
+      .treeMap({
+        terms: [
+          {
+            column: 'provider',
+            value: type,
+          },
+        ],
+      })
+      .then((res) => {
+        // console.log(res.result)
+        setData(res.result);
+        setExpandedKey([res.result?.[0].id]);
+      });
+    // console.log(metaData,'metaData')
+  }, []);
+
+  useEffect(() => {
+    setList(filterRef.current);
+  }, [filterRef.current]);
+
+  return (
+    <Modal
+      title="批量映射"
+      visible
+      onCancel={() => {
+        close();
+      }}
+      onOk={() => {
+        if (list && list.length !== 0) {
+          save();
+        } else {
+          onlyMessage('请选择采集器', 'warning');
+        }
+      }}
+      width="900px"
+    >
+      <div className="map-tree">
+        <div className="map-tree-top">
+          采集器的点位名称与属性名称一致时将自动映射绑定;有多个采集器点位名称与属性名称一致时以第1个采集器的点位数据进行绑定
+        </div>
+        <div className="map-tree-content">
+          <Card title="源数据" className="map-tree-card">
+            <Tree
+              key={'id'}
+              checkable
+              selectable={false}
+              expandedKeys={expandedKey}
+              onExpand={(expandedKeys) => {
+                setExpandedKey(expandedKeys);
+              }}
+              onCheck={(checkeds) => {
+                setChecked(checkeds);
+              }}
+            >
+              {data?.map((item: any) => (
+                <Tree.TreeNode key={item.id} title={item.name} checkable={false}>
+                  {(item?.collectors || []).map((collector: any) => (
+                    <Tree.TreeNode key={collector.id} title={collector.name}>
+                      {(collector?.points || []).map((i: any) => (
+                        <Tree.TreeNode checkable={false} key={i.id} title={i.name}></Tree.TreeNode>
+                      ))}
+                    </Tree.TreeNode>
+                  ))}
+                </Tree.TreeNode>
+              ))}
+            </Tree>
+          </Card>
+          <div>
+            <Button
+              disabled={checked && checked.length === 0}
+              onClick={() => {
+                const item = filterTree(data, checked);
+                setData(item);
+              }}
+            >
+              加入右侧
+            </Button>
+          </div>
+          <Card title="采集器" className="map-tree-card">
+            <List
+              size="small"
+              dataSource={list}
+              renderItem={(item: any) => (
+                <List.Item
+                  actions={[
+                    <Popconfirm
+                      title="确定删除?"
+                      onConfirm={() => {
+                        pushTree(item);
+                      }}
+                    >
+                      <DeleteOutlined />
+                    </Popconfirm>,
+                  ]}
+                >
+                  {item.name}
+                </List.Item>
+              )}
+            />
+          </Card>
+        </div>
+      </div>
+    </Modal>
+  );
+};
+export default ChannelTree;

+ 40 - 6
src/pages/device/Instance/Detail/MapChannel/index.tsx

@@ -10,6 +10,8 @@ import Service from './service';
 import { action } from '@formily/reactive';
 import type { Response } from '@/utils/typings';
 import './index.less';
+import { onlyMessage } from '@/utils/util';
+import ChannelTree from './Tree';
 
 interface Props {
   type: 'MODBUS_TCP' | 'OPC_UA';
@@ -24,6 +26,7 @@ const MapChannel = (props: Props) => {
   const [reload, setReload] = useState<string>('');
   const [properties, setProperties] = useState<any>([]);
   const [channelList, setChannelList] = useState<any>([]);
+  const [visible, setVisible] = useState<boolean>(false);
 
   const Render = (propsName: any) => {
     const text = properties.find((item: any) => item.metadataId === propsName.value);
@@ -36,6 +39,13 @@ const MapChannel = (props: Props) => {
       return <Badge status="error" text={'未绑定'} />;
     }
   };
+  const remove = async (params: any) => {
+    const res = await service.removeMap('device', data.id, [params]);
+    if (res.status === 200) {
+      onlyMessage('解绑成功');
+      setReload('remove');
+    }
+  };
   const ActionButton = () => {
     const record = ArrayTable.useRecord?.();
     const index = ArrayTable.useIndex?.();
@@ -51,7 +61,7 @@ const MapChannel = (props: Props) => {
           title: '确认解绑',
           disabled: !record(index)?.id,
           onConfirm: async () => {
-            // remove(record(index)?.id);
+            remove(record(index)?.id);
           },
         }}
         key="unbind"
@@ -343,6 +353,14 @@ const MapChannel = (props: Props) => {
     },
   };
 
+  const save = async (item: any) => {
+    const res = await service.saveMap(data.id, type, item);
+    if (res.status === 200) {
+      onlyMessage('保存成功');
+      setReload('save');
+    }
+  };
+
   useEffect(() => {
     service
       .getChannel({
@@ -375,6 +393,7 @@ const MapChannel = (props: Props) => {
       metadataId: item.id,
       metadataName: `${item.name}(${item.id})`,
       metadataType: 'property',
+      name: item.name,
     }));
     if (metadata && metadata.length !== 0) {
       service.getMap('device', data.id).then((res) => {
@@ -395,7 +414,7 @@ const MapChannel = (props: Props) => {
           const delList = array.filter((a: any) => !a.metadataName).map((b: any) => b.id);
           //删除后解绑
           if (delList && delList.length !== 0) {
-            // service.removeDevicePoint(data.id, delList);
+            service.removeMap('device', data.id, delList);
           }
         }
       });
@@ -411,17 +430,21 @@ const MapChannel = (props: Props) => {
       ) : (
         <>
           <div className="top-button">
-            <Button style={{ marginRight: 10 }} onClick={async () => {}}>
+            <Button
+              style={{ marginRight: 10 }}
+              onClick={async () => {
+                setVisible(true);
+              }}
+            >
               批量映射
             </Button>
             <Button
               type="primary"
               onClick={async () => {
-                setReload('');
-                console.log(type);
                 const value: any = await form.submit();
                 if (value) {
-                  console.log(value);
+                  const array = value.requestList.filter((item: any) => item.channelId);
+                  save(array);
                 }
               }}
             >
@@ -433,6 +456,17 @@ const MapChannel = (props: Props) => {
           </FormProvider>
         </>
       )}
+      {visible && (
+        <ChannelTree
+          close={() => {
+            setVisible(false);
+            setReload('map');
+          }}
+          deviceId={data.id}
+          metaData={properties}
+          type={type}
+        />
+      )}
     </Card>
   );
 };

+ 15 - 10
src/pages/device/Instance/Detail/MapChannel/service.ts

@@ -28,16 +28,21 @@ class Service extends BaseService<any> {
       method: 'POST',
       data,
     });
-  treeMap = (deviceId: string, data?: any) =>
-    request(
-      `/${SystemConst.API_BASE}/edge/operations/${deviceId}/data-collector-channel-tree/invoke`,
-      {
-        method: 'POST',
-        data,
-      },
-    );
-  saveMap = (thingType: string, thingId: any, data?: any) =>
-    request(`/${SystemConst.API_BASE}/things/collector/${thingType}/${thingId}/{provider}`, {
+  // treeMap = (data?: any) =>
+  //   request(
+  //     `/${SystemConst.API_BASE}/data-collect/channel/_all/tree`,
+  //     {
+  //       method: 'POST',
+  //       data: data,
+  //     },
+  //   );
+  treeMap = (data: any) =>
+    request(`/${SystemConst.API_BASE}/data-collect/channel/_all/tree`, {
+      method: 'POST',
+      data: data,
+    });
+  saveMap = (thingId: any, provider: string, data?: any) =>
+    request(`/${SystemConst.API_BASE}/things/collector/device/${thingId}/${provider}`, {
       method: 'PATCH',
       data: data,
     });