Commit bfd56bed0c1a0292ef2edcb0b1f2551471f38f9f

Authored by ww
1 parent dbe62be1

fix: data component control component send command

  1 +import { DeviceProfileModel } from '../../device/model/deviceModel';
1 2 import { HistoryData } from './model';
2 3 import { defHttp } from '/@/utils/http/axios';
3 4
4 5 // 获取设备配置
5 6 export const getDeviceProfile = () => {
6   - return defHttp.get({
  7 + return defHttp.get<DeviceProfileModel[]>({
7 8 url: '/device_profile/me/list',
8 9 });
9 10 };
... ...
... ... @@ -166,9 +166,10 @@ export const getShareBoardComponentInfo = (params: { boardId: string; tenantId:
166 166 * @param params
167 167 * @returns
168 168 */
169   -export const getAllDeviceByOrg = (params: string) => {
  169 +export const getAllDeviceByOrg = (organizationId: string, deviceProfileId?: string) => {
170 170 return defHttp.get<MasterDeviceList[]>({
171   - url: `${DeviceUrl.GET_DEVICE}/${params}`,
  171 + url: `${DeviceUrl.GET_DEVICE}/${organizationId}`,
  172 + params: { deviceProfileId },
172 173 });
173 174 };
174 175
... ...
... ... @@ -78,6 +78,7 @@ export interface DataSource {
78 78 gatewayDevice: boolean;
79 79 componentInfo: ComponentInfo;
80 80 deviceName: string;
  81 + deviceProfileId: string;
81 82
82 83 // front usage
83 84 uuid?: string;
... ...
... ... @@ -25,6 +25,7 @@ export interface StructJSON {
25 25 identifier?: string;
26 26 remark?: string;
27 27 dataType?: DataType;
  28 + serviceCommand?: string;
28 29 }
29 30
30 31 export interface FunctionJson {
... ...
... ... @@ -65,7 +65,7 @@ export const releaseModel = (deviceProfileId: string) => {
65 65
66 66 export const getModelServices = (params: { deviceProfileId: string }) => {
67 67 const { deviceProfileId } = params;
68   - return defHttp.get({
  68 + return defHttp.get<ModelOfMatterParams[]>({
69 69 url: `${ModelOfMatter.GET_MODEL_SERVICE}/${deviceProfileId}`,
70 70 });
71 71 };
... ...
... ... @@ -27,7 +27,7 @@
27 27 const _value = (event.target as HTMLInputElement).checked;
28 28 emit('update:value', _value);
29 29 emit('change', _value);
30   - sendCommand(props.value?.slaveDeviceId || props.value?.deviceId, _value);
  30 + sendCommand(props.value!, _value);
31 31 };
32 32 </script>
33 33
... ...
... ... @@ -32,8 +32,7 @@
32 32
33 33 const { sendCommand } = useSendCommand();
34 34 const handleChange = (value: boolean) => {
35   - console.log(props.value);
36   - sendCommand(props.value.slaveDeviceId! || props.value.deviceId!, value);
  35 + sendCommand(props.value, value);
37 36 };
38 37
39 38 watchEffect(() => {
... ...
... ... @@ -22,7 +22,7 @@
22 22 const _value = (event.target as HTMLInputElement).checked;
23 23 emit('update:value', _value);
24 24 emit('change', _value);
25   - sendCommand(props.value?.slaveDeviceId || props.value?.deviceId, _value);
  25 + sendCommand(props.value!, _value);
26 26 };
27 27
28 28 const getRadio = computed(() => {
... ...
... ... @@ -13,6 +13,7 @@ export interface ControlComponentValue {
13 13 deviceId?: string;
14 14 fontColor?: string;
15 15 slaveDeviceId?: string;
  16 + deviceProfileId?: string;
16 17 }
17 18
18 19 export const ControlComponentDefaultConfig: ControlComponentValue = {
... ... @@ -31,6 +32,7 @@ export const transformControlConfig = (
31 32 ...dataSourceRecord.componentInfo,
32 33 attribute: dataSourceRecord.attribute,
33 34 attributeRename: dataSourceRecord.attributeRename,
  35 + deviceProfileId: dataSourceRecord.deviceProfileId,
34 36 deviceId: dataSourceRecord.deviceId,
35 37 slaveDeviceId: dataSourceRecord.slaveDeviceId,
36 38 } as ControlComponentValue,
... ...
  1 +import { ControlComponentValue } from './control.config';
  2 +import { getDeviceProfile } from '/@/api/alarm/position';
1 3 import { sendCommandOneway } from '/@/api/dataBoard';
  4 +import { getModelServices } from '/@/api/device/modelOfMatter';
2 5 import { useMessage } from '/@/hooks/web/useMessage';
  6 +import { isString } from '/@/utils/is';
3 7
4 8 const { createMessage } = useMessage();
5 9 export function useSendCommand() {
6   - const sendCommand = async (deviceId: string, value: any) => {
  10 + const sendCommand = async (record: ControlComponentValue, value: any) => {
  11 + const { deviceId, deviceProfileId, attribute } = record;
7 12 if (!deviceId) return;
8 13 try {
  14 + const list = await getDeviceProfile();
  15 + const deviceProfile = list.find((item) => item.id === deviceProfileId);
  16 + if (!deviceProfile) return;
  17 + let params: string | Recordable = {
  18 + [attribute!]: Number(value),
  19 + };
  20 + if (deviceProfile.transportType === 'TCP') {
  21 + const serviceList = await getModelServices({ deviceProfileId: deviceProfileId! });
  22 + const record = serviceList.find((item) => item.identifier === attribute);
  23 + const sendCommand = record?.functionJson.inputData?.at(0)?.serviceCommand || '';
  24 + params = isString(sendCommand) ? sendCommand : JSON.stringify(sendCommand);
  25 + }
9 26 await sendCommandOneway({
10 27 deviceId,
11 28 value: {
12   - params: Number(value),
  29 + params: params,
13 30 persistent: true,
14 31 additionalInfo: {
15 32 cmdType: 'API',
... ...
... ... @@ -5,9 +5,9 @@
5 5 SettingOutlined,
6 6 SwapOutlined,
7 7 } from '@ant-design/icons-vue';
8   - import { Tooltip, Button } from 'ant-design-vue';
  8 + import { Tooltip, Button, Alert } from 'ant-design-vue';
9 9 import { FormActionType, useForm } from '/@/components/Form';
10   - import { basicSchema, DataSourceField } from '../config/basicConfiguration';
  10 + import { basicSchema, DataSourceField, isMapComponent } from '../config/basicConfiguration';
11 11 import BasicForm from '/@/components/Form/src/BasicForm.vue';
12 12 import { ref, shallowReactive, unref, nextTick, watch, computed, onMounted } from 'vue';
13 13 import VisualOptionsModal from './VisualOptionsModal.vue';
... ... @@ -22,6 +22,7 @@
22 22 import { useSortable } from '/@/hooks/web/useSortable';
23 23 import { cloneDeep } from 'lodash-es';
24 24 import { frontComponentMap } from '../../components/help';
  25 + import { isControlComponent } from '../config/basicConfiguration';
25 26
26 27 type DataSourceFormEL = { [key: string]: Nullable<FormActionType> };
27 28
... ... @@ -250,6 +251,14 @@
250 251 inited = true;
251 252 }
252 253
  254 + const isControlCmp = computed(() => {
  255 + return isControlComponent(props.frontId as FrontComponent);
  256 + });
  257 +
  258 + const isMapCmp = computed(() => {
  259 + return isMapComponent(props.frontId as FrontComponent);
  260 + });
  261 +
253 262 onMounted(() => handleSort());
254 263
255 264 defineExpose({
... ... @@ -264,7 +273,25 @@
264 273 <div class="w-3/4">
265 274 <BasicForm @register="basicRegister" class="w-full" />
266 275 </div>
  276 + <Alert type="info" show-icon v-if="isControlCmp">
  277 + <template #description>
  278 + <div>
  279 + 控制组件数据源为TCP产品,则其控制命令下发为TCP产品 物模型=>服务,且不具备状态显示功能.
  280 + </div>
  281 + <div>
  282 + 控制组件数据源为非TCP产品,则其控制命令下发为产品 物模型=>属性,且具备状态显示功能.
  283 + </div>
  284 + </template>
  285 + </Alert>
  286 + <Alert type="info" show-icon v-if="isMapCmp">
  287 + <template #description>
  288 + <div>
  289 + 地图组件,需绑定两个数据源,且数据源为同一设备。第一数据源为经度,第二数据源为维度,否则地图组件不能正常显示。
  290 + </div>
  291 + </template>
  292 + </Alert>
267 293 <h3 class="w-24 flex-shrink-0 text-right pr-2 my-4">选择数据源</h3>
  294 +
268 295 <section ref="formListEl">
269 296 <div v-for="item in dataSource" :data-id="item.id" :key="item.id" class="flex bg-light-50">
270 297 <div class="w-24 text-right flex right justify-end"> 选择设备 </div>
... ...
... ... @@ -4,6 +4,8 @@ import { FormSchema } from '/@/components/Form';
4 4 import { copyTransFun } from '/@/utils/fnUtils';
5 5 import { DeviceAttributeParams, MasterDeviceList } from '/@/api/dataBoard/model';
6 6 import { FrontComponent } from '../../const/const';
  7 +import { getDeviceProfile } from '/@/api/alarm/position';
  8 +import { getModelServices } from '/@/api/device/modelOfMatter';
7 9
8 10 export enum BasicConfigField {
9 11 NAME = 'name',
... ... @@ -18,13 +20,22 @@ const getDeviceAttribute = async (params: DeviceAttributeParams) => {
18 20 return [];
19 21 };
20 22
  23 +const getDeviceService = async (deviceProfileId: string) => {
  24 + try {
  25 + const data = await getModelServices({ deviceProfileId });
  26 + if (data)
  27 + return data.map((item) => ({ ...item, label: item.functionName, value: item.identifier }));
  28 + } catch (error) {}
  29 + return [];
  30 +};
  31 +
21 32 export enum DataSourceField {
22 33 IS_GATEWAY_DEVICE = 'gatewayDevice',
  34 + TRANSPORT_TYPE = 'transportType',
23 35 ORIGINATION_ID = 'organizationId',
24 36 DEVICE_ID = 'deviceId',
25 37 SLAVE_DEVICE_ID = 'slaveDeviceId',
26 38 DEVICE_PROFILE_ID = 'deviceProfileId',
27   - SLAVE_DEVICE_PROFILE_ID = 'slaveDeviceProfileId',
28 39 ATTRIBUTE = 'attribute',
29 40 ATTRIBUTE_RENAME = 'attributeRename',
30 41 DEVICE_NAME = 'deviceName',
... ... @@ -33,16 +44,25 @@ export enum DataSourceField {
33 44 LATITUDE_ATTRIBUTE = 'latitudeAttribute',
34 45 }
35 46
36   -const isControlComponent = (frontId: FrontComponent) => {
  47 +export const isControlComponent = (frontId: FrontComponent) => {
37 48 const list = [
38 49 FrontComponent.CONTROL_COMPONENT_SLIDING_SWITCH,
39 50 FrontComponent.CONTROL_COMPONENT_SWITCH_WITH_ICON,
40 51 FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH,
41 52 ];
42   - if (list.includes(frontId)) {
43   - return true;
44   - }
45   - return false;
  53 + return list.includes(frontId);
  54 +};
  55 +
  56 +export const isMapComponent = (frontId: FrontComponent) => {
  57 + const list = [
  58 + FrontComponent.MAP_COMPONENT_TRACK_HISTORY,
  59 + FrontComponent.MAP_COMPONENT_TRACK_REAL,
  60 + ];
  61 + return list.includes(frontId);
  62 +};
  63 +
  64 +const isTcpProfile = (transportType: string) => {
  65 + return transportType === 'TCP';
46 66 };
47 67
48 68 export const basicSchema: FormSchema[] = [
... ... @@ -81,6 +101,45 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
81 101 show: false,
82 102 },
83 103 {
  104 + field: DataSourceField.TRANSPORT_TYPE,
  105 + component: 'Input',
  106 + label: '设备配置类型',
  107 + show: false,
  108 + },
  109 + {
  110 + field: DataSourceField.DEVICE_PROFILE_ID,
  111 + component: 'ApiSelect',
  112 + label: '产品',
  113 + colProps: { span: 8 },
  114 + rules: [{ required: true, message: '产品为必填项' }],
  115 + componentProps: ({ formActionType, formModel }) => {
  116 + const { setFieldsValue } = formActionType;
  117 + const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
  118 + return {
  119 + api: async () => {
  120 + const list = await getDeviceProfile();
  121 + if (deviceProfileId) {
  122 + const record = list.find((item) => item.id === deviceProfileId);
  123 + setFieldsValue({ [DataSourceField.TRANSPORT_TYPE]: record?.transportType });
  124 + }
  125 + return list;
  126 + },
  127 + labelField: 'name',
  128 + valueField: 'id',
  129 + onChange: (_, option: Record<'transportType', string>) => {
  130 + console.log(option);
  131 + setFieldsValue({
  132 + [DataSourceField.DEVICE_ID]: null,
  133 + [DataSourceField.ATTRIBUTE]: null,
  134 + [DataSourceField.SLAVE_DEVICE_ID]: null,
  135 + [DataSourceField.IS_GATEWAY_DEVICE]: false,
  136 + [DataSourceField.TRANSPORT_TYPE]: option.transportType,
  137 + });
  138 + },
  139 + };
  140 + },
  141 + },
  142 + {
84 143 field: DataSourceField.ORIGINATION_ID,
85 144 component: 'ApiTreeSelect',
86 145 label: '组织',
... ... @@ -101,8 +160,6 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
101 160 [DataSourceField.ATTRIBUTE]: null,
102 161 [DataSourceField.SLAVE_DEVICE_ID]: null,
103 162 [DataSourceField.IS_GATEWAY_DEVICE]: false,
104   - [DataSourceField.DEVICE_PROFILE_ID]: null,
105   - [DataSourceField.SLAVE_DEVICE_PROFILE_ID]: null,
106 163 });
107 164 },
108 165 getPopupContainer: () => document.body,
... ... @@ -124,16 +181,13 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
124 181 componentProps({ formModel, formActionType }) {
125 182 const { setFieldsValue } = formActionType;
126 183 const organizationId = formModel[DataSourceField.ORIGINATION_ID];
127   - const deviceId = formModel[DataSourceField.DEVICE_ID];
  184 + const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
  185 +
128 186 return {
129 187 api: async () => {
130 188 if (organizationId) {
131 189 try {
132   - const data = await getAllDeviceByOrg(organizationId);
133   - if (deviceId) {
134   - const record = data.find((item) => item.id === deviceId);
135   - setFieldsValue({ [DataSourceField.DEVICE_PROFILE_ID]: record?.deviceProfileId });
136   - }
  190 + const data = await getAllDeviceByOrg(organizationId, deviceProfileId);
137 191 if (data)
138 192 return data.map((item) => ({
139 193 ...item,
... ... @@ -150,9 +204,7 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
150 204 setFieldsValue({
151 205 [DataSourceField.ATTRIBUTE]: null,
152 206 [DataSourceField.IS_GATEWAY_DEVICE]: record?.deviceType === 'GATEWAY',
153   - [DataSourceField.DEVICE_PROFILE_ID]: record?.deviceProfileId,
154 207 [DataSourceField.SLAVE_DEVICE_ID]: null,
155   - [DataSourceField.SLAVE_DEVICE_PROFILE_ID]: null,
156 208 [DataSourceField.DEVICE_NAME]: record?.label,
157 209 });
158 210 },
... ... @@ -162,19 +214,18 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
162 214 },
163 215 },
164 216 {
165   - field: DataSourceField.SLAVE_DEVICE_PROFILE_ID,
166   - component: 'Input',
167   - label: '',
168   - show: false,
169   - },
170   - {
171 217 field: DataSourceField.SLAVE_DEVICE_ID,
172 218 label: '网关子设备',
173 219 component: 'ApiSelect',
174 220 colProps: { span: 8 },
175 221 rules: [{ required: true, message: '网关子设备为必填项' }],
  222 + show: false,
176 223 ifShow({ model }) {
177   - return model[DataSourceField.IS_GATEWAY_DEVICE];
  224 + const transportType = model[DataSourceField.TRANSPORT_TYPE];
  225 + return (
  226 + !(isControlComponent(frontId as FrontComponent) && isTcpProfile(transportType)) &&
  227 + model[DataSourceField.IS_GATEWAY_DEVICE]
  228 + );
178 229 },
179 230 dynamicRules({ model }) {
180 231 return [
... ... @@ -186,16 +237,12 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
186 237 const organizationId = formModel[DataSourceField.ORIGINATION_ID];
187 238 const isGatewayDevice = formModel[DataSourceField.IS_GATEWAY_DEVICE];
188 239 const deviceId = formModel[DataSourceField.DEVICE_ID];
189   - const slaveDeviceId = formModel[DataSourceField.SLAVE_DEVICE_ID];
  240 +
190 241 return {
191 242 api: async () => {
192 243 if (organizationId && isGatewayDevice) {
193 244 try {
194 245 const data = await getGatewaySlaveDevice({ organizationId, masterId: deviceId });
195   - if (slaveDeviceId) {
196   - const record = data.find((item) => item.id === slaveDeviceId);
197   - setFieldsValue({ [DataSourceField.DEVICE_PROFILE_ID]: record?.deviceProfileId });
198   - }
199 246 if (data)
200 247 return data.map((item) => ({
201 248 ...item,
... ... @@ -210,7 +257,6 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
210 257 onChange(_value, record: MasterDeviceList) {
211 258 setFieldsValue({
212 259 [DataSourceField.ATTRIBUTE]: null,
213   - [DataSourceField.SLAVE_DEVICE_PROFILE_ID]: record.deviceProfileId,
214 260 [DataSourceField.DEVICE_NAME]: record?.label,
215 261 });
216 262 },
... ... @@ -226,33 +272,30 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
226 272 colProps: { span: 8 },
227 273 rules: [{ required: true, message: '属性为必填项' }],
228 274 componentProps({ formModel }) {
229   - const isGatewayDevice = formModel[DataSourceField.IS_GATEWAY_DEVICE];
230   - const deviceId = formModel[DataSourceField.DEVICE_ID];
231 275 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
232   - const slaveDeviceId = formModel[DataSourceField.SLAVE_DEVICE_ID];
233   - const slaveDeviceProfileId = formModel[DataSourceField.SLAVE_DEVICE_PROFILE_ID];
  276 + const transportType = formModel[DataSourceField.TRANSPORT_TYPE];
234 277
235 278 return {
236 279 api: async () => {
237   - if (deviceId) {
238   - try {
239   - if (isGatewayDevice && slaveDeviceId && slaveDeviceProfileId) {
240   - return await getDeviceAttribute({
241   - deviceProfileId: slaveDeviceProfileId,
242   - dataType: isControlComponent(frontId!) ? 'BOOL' : undefined,
243   - });
244   - }
245   - if (!isGatewayDevice && deviceProfileId) {
246   - return await getDeviceAttribute({
247   - deviceProfileId,
248   - dataType: isControlComponent(frontId!) ? 'BOOL' : undefined,
249   - });
250   - }
251   - } catch (error) {}
252   - }
  280 + try {
  281 + if (isControlComponent(frontId as FrontComponent) && isTcpProfile(transportType)) {
  282 + return await getDeviceService(deviceProfileId);
  283 + }
  284 +
  285 + if (deviceProfileId) {
  286 + return await getDeviceAttribute({
  287 + deviceProfileId,
  288 + dataType: isControlComponent(frontId!) ? 'BOOL' : undefined,
  289 + });
  290 + }
  291 + } catch (error) {}
  292 +
253 293 return [];
254 294 },
255   - placeholder: '请选择属性',
  295 + placeholder:
  296 + isControlComponent(frontId as FrontComponent) && isTcpProfile(transportType)
  297 + ? '请选择服务'
  298 + : '请选择属性',
256 299 getPopupContainer: () => document.body,
257 300 };
258 301 },
... ...