@cpzxrobot/sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,19 @@
1
+ //消息助手网关,获取消息助手的信息
2
+ export class AssistantGateway extends Object {
3
+ context: Shzx
4
+ constructor(context: Shzx) {
5
+ super()
6
+ this.context = context
7
+ }
8
+
9
+ getByLoginUser() {
10
+ return this.context.ready.then((axios) => {
11
+ return axios.get('/api/v1/robot/wx').then((res) => {
12
+ if (res.data.Error) {
13
+ throw res.data.Error
14
+ }
15
+ return res.data
16
+ })
17
+ })
18
+ }
19
+ }
@@ -0,0 +1,28 @@
1
+ export class CameraGateway extends Object {
2
+ context: Shzx
3
+ constructor(context: Shzx) {
4
+ super()
5
+ this.context = context
6
+ }
7
+ //搜索摄像头
8
+ async search(data: Unit): Promise<any> {
9
+ await this.context.ready
10
+ const response = await this.context.axios.post('/api/v1/camera/search', {
11
+ unit_id: data.id
12
+ })
13
+ return response.data
14
+ }
15
+
16
+ //获得摄像头的播放地址
17
+ async getUrl(data: Camera): Promise<any> {
18
+ await this.context.ready
19
+ const response = await this.context.axios.get(`/api/v1/camera/${data.id}/url/wss`)
20
+ return response.data
21
+ }
22
+ //获取工厂天气
23
+ async weather(id: number): Promise<any> {
24
+ await this.context.ready
25
+ const response = await this.context.axios.get(`/api/v1/weather?code=${id}`)
26
+ return response.data
27
+ }
28
+ }
@@ -0,0 +1,318 @@
1
+ import type { AxiosResponse } from "axios";
2
+
3
+ export interface DataQueryArgs {
4
+ start?: string;
5
+ stop?: string;
6
+ type?:
7
+ | "diffPerDay"
8
+ | "sumPerDay"
9
+ | "latest"
10
+ | "latestInRangePerDay"
11
+ | "last"
12
+ | "difference";
13
+ period?: "1mo" | "1d" | null;
14
+ [key: string]: any;
15
+ }
16
+
17
+ export abstract class deviceFilter<T extends { id: number }> {
18
+ context: Shzx;
19
+ protected devices: T[] = [];
20
+
21
+ // private getDetail!: (info: T, id: number, context: Shzx) => Promise<any> | null
22
+ // axios: MyAxiosInstance;
23
+ // user: UserGateway;
24
+ // ready: Promise<boolean>;
25
+
26
+ constructor(context: Shzx) {
27
+ this.context = context;
28
+ }
29
+
30
+ abstract get deviceType(): string | null;
31
+
32
+ //如果子类有更多其他属性需要附加到属性列表中,需要实现该方法
33
+ getDetail(info: T, _id: number): Promise<any> {
34
+ return Promise.resolve(info);
35
+ }
36
+
37
+ //如果子类需要对返回的数据进行包装,需要实现该方法
38
+ wrapData(_id: number, _args: DataQueryArgs, p: Promise<any>): Promise<any> {
39
+ return p;
40
+ }
41
+
42
+ wrapList(list: T[]): Promise<T[]> {
43
+ return Promise.resolve(list);
44
+ }
45
+
46
+ getInfo(id: number): Promise<T> {
47
+ if (this.devices) {
48
+ const one = this.devices.find((device) => device.id == id);
49
+ if (one) {
50
+ return Promise.resolve(one);
51
+ }
52
+ }
53
+ return this.context.axios
54
+ .get(`/api/v1/device/${id}/detail?latest=true`)
55
+ .then((res) => {
56
+ if (res.data.Error) {
57
+ throw res.data.Error;
58
+ }
59
+ return res.data;
60
+ });
61
+ }
62
+
63
+ //获得单个设备的详细信息和状态,包括设备的基本信息和跟业务类型相关的附加状态信息
64
+ //不应单独使用getInfo和getDetail,应使用getStatus
65
+ getStatus(id: number) {
66
+ return this.context.ready
67
+ .then(() => this.getInfo(id))
68
+ .then((info) => {
69
+ return this.getDetail(info, id);
70
+ });
71
+ }
72
+
73
+ // 标记设备状态,部分类型的设备支持标记状态,比如喂料塔可以标记为零点已校准
74
+ markStatus(id: number, name: string) {
75
+ let action: string;
76
+ switch (name) {
77
+ case "zero-calibrated": // 标记为零点已校准状态
78
+ action = "/updateZeroTime";
79
+ break;
80
+ default:
81
+ throw new Error("Method not implemented.");
82
+ }
83
+ return this.context.ready.then(() =>
84
+ this.context.axios
85
+ .post("/api/v1/pigfarm/device/status" + action + "?id=" + id)
86
+ .then((res) => {
87
+ if (res.data.code != 200) {
88
+ throw res.data.message;
89
+ }
90
+ return res.data;
91
+ })
92
+ );
93
+ }
94
+
95
+ update(deviceId: number, cycle?: number, towerVolume?: number) {
96
+ return this.context.ready.then(() => {
97
+ this.context.axios
98
+ .post("/api/v1/pigfarm/device/status/update", {
99
+ deviceId,
100
+ cycle,
101
+ towerVolume,
102
+ })
103
+ .then((res) => {
104
+ if (res.data.code != 200) {
105
+ throw res.data.message;
106
+ }
107
+ return res.data;
108
+ });
109
+ });
110
+ }
111
+
112
+ getData(
113
+ id: number,
114
+ args: DataQueryArgs = {
115
+ start: "",
116
+ stop: "",
117
+ type: "diffPerDay", // diffPerDay, sumPerDay, latest
118
+ }
119
+ ) {
120
+ if (args.stop == "") {
121
+ args.stop = this.formateDateToYYYYMMDD(new Date());
122
+ }
123
+ if (args.start == "") {
124
+ const sevenDaysAgo = new Date();
125
+ sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
126
+ args.start = this.formateDateToYYYYMMDD(sevenDaysAgo);
127
+ }
128
+
129
+ return this.context.ready
130
+ .then(() => {
131
+ const p1 = this.context.axios.get(
132
+ `/api/v1/device/${id}/data/${args.type}`,
133
+ {
134
+ params: args,
135
+ }
136
+ );
137
+
138
+ return this.wrapData(id, args, p1);
139
+ })
140
+ .then((res: AxiosResponse) => {
141
+ if (res.data.Error) {
142
+ throw res.data.Error;
143
+ }
144
+ // this.devices.push(res.data);
145
+ return res.data;
146
+ });
147
+ }
148
+
149
+ //按月获取数据:period = '1mo',按天获取数据:period = '1d'
150
+ //获取数据类型:latestInRangePerDay(按周期获得每天的数据,只用于峰谷平)
151
+ // 不再使用:diffPerDay(每天差值),sumPerDay(每日汇总),last(每个周期最新)
152
+ // difference(每个周期差值,根据period的不同,差值的计算方式不同
153
+ getHistoryDatas(
154
+ ids: number[],
155
+ args: DataQueryArgs = {
156
+ start: "",
157
+ stop: "",
158
+ period: null,
159
+ type: "latestInRangePerDay",
160
+ }
161
+ ) {
162
+ // 对period = '1mo'的情况, 进行特殊优化
163
+ if (args.period == "1mo") {
164
+ //按start获得每个月
165
+ let d;
166
+ if (args.start && args.start![0] == "-") {
167
+ //args.start like '-1y'
168
+ const matcher = args.start.match(/-(\d+)(\w+)/);
169
+ if (matcher) {
170
+ d = new Date();
171
+ const num = parseInt(matcher[1]);
172
+ const unit = matcher[2];
173
+ if (unit == "y") {
174
+ d.setFullYear(d.getFullYear() - num);
175
+ } else if (unit == "m") {
176
+ d.setMonth(d.getMonth() - num);
177
+ } else if (unit == "d") {
178
+ d.setDate(d.getDate() - num);
179
+ }
180
+ }
181
+ } else {
182
+ //args.start is a date string
183
+ d = new Date(args.start!);
184
+ }
185
+ //获取从d开始,到现在的每个月的数据
186
+ const now = new Date();
187
+ const months: string[] = [];
188
+ while (d! < now) {
189
+ months.push(this.formateDateToYYYYMM(d!));
190
+ d!.setMonth(d!.getMonth() + 1);
191
+ }
192
+ return this.context.ready.then((axios) => {
193
+ const allPs = ids.map((id) => {
194
+ const promises = months.map((month) => {
195
+ return axios
196
+ .get(`/api/v1/device/history/monthly/${month}/${args.type}/${id}`)
197
+ .then((res) => {
198
+ return {
199
+ month: month,
200
+ id: id,
201
+ data: res.data,
202
+ };
203
+ });
204
+ });
205
+ return Promise.all(promises).then((res) => {
206
+ return res.reduce((acc, cur) => {
207
+ const singleIds = acc[cur.id] || {};
208
+ const values = Object.values(cur.data);
209
+ if (values.length > 0) {
210
+ singleIds[cur.month] = values[0];
211
+ }
212
+ acc[cur.id] = singleIds;
213
+ return acc;
214
+ }, {} as any);
215
+ });
216
+ }, {} as any);
217
+ return Promise.all(allPs).then((res) => {
218
+ const data = res.reduce((acc, cur) => {
219
+ return {
220
+ ...acc,
221
+ ...cur,
222
+ };
223
+ }, {} as any);
224
+ return {
225
+ data,
226
+ };
227
+ });
228
+ });
229
+ }
230
+
231
+ return this.context.ready.then((axios) => {
232
+ return axios.post("/api/v1/device/history", {
233
+ device_ids: ids,
234
+ ...args,
235
+ });
236
+ });
237
+ }
238
+
239
+ formateDateToYYYYMMDD(time: Date) {
240
+ const year = time.getFullYear();
241
+ const month = time.getMonth() + 1; // getMonth() 返回的月份从0开始
242
+ const date = time.getDate();
243
+
244
+ // 使用 `padStart` 方法来确保月份和日期总是两位数
245
+ return `${year}-${String(month).padStart(2, "0")}-${String(date).padStart(
246
+ 2,
247
+ "0"
248
+ )}`;
249
+ }
250
+
251
+ formateDateToYYYYMM(time: Date) {
252
+ const year = time.getFullYear();
253
+ const month = time.getMonth() + 1; // getMonth() 返回的月份从0开始
254
+ // const date = time.getDate()
255
+
256
+ // 使用 `padStart` 方法来确保月份和日期总是两位数
257
+ return `${year}-${String(month).padStart(2, "0")}`;
258
+ }
259
+
260
+ //获取设备列表,如果options.id为空,则获取用户选中工厂的设备列表,否则获取指定id的工厂的设备
261
+ //last:获取最新数据
262
+ // rangeToday:获取今天的第一个数据和最后一个数据
263
+ list(
264
+ options:
265
+ | undefined
266
+ | {
267
+ id?: Number | null | undefined;
268
+ data?:
269
+ | "diffPerDay"
270
+ | "sumPerDay"
271
+ | "latest"
272
+ | "weekoverweek"
273
+ | "last"
274
+ | "rangeToday"
275
+ | null; //如果data为null,则不获取数据,只获取设备列表,否则获取设备列表和数据
276
+ status?: string;
277
+ unit?: Number;
278
+ } = undefined
279
+ ): Promise<T[]> {
280
+ // if (this.devices.length > 0) {
281
+ // return Promise.resolve(this.devices)
282
+ // } else {
283
+ let p;
284
+ if (options?.id) {
285
+ p = Promise.resolve({ id: options.id });
286
+ } else {
287
+ p = this.context.ready.then(() => {
288
+ return this.context.user.getSelectedFarm();
289
+ });
290
+ }
291
+ const rand = Math.random();
292
+ const type = this.deviceType;
293
+ return p
294
+ .then((farm) => {
295
+ console.log("type", type);
296
+ return this.context.axios.get("/api/v1/device/list?rand=" + rand, {
297
+ params: {
298
+ location: farm,
299
+ type: this.deviceType,
300
+ unit: options?.unit,
301
+ data: options?.data,
302
+ status: options?.status,
303
+ },
304
+ });
305
+ })
306
+ .then((res) => {
307
+ if (res.data.Error) {
308
+ throw res.data.Error;
309
+ } else {
310
+ if (options == undefined || options.id == undefined) {
311
+ //当id为空时(获取用户选中工厂的设备),将所有设备保存到devices中
312
+ this.devices = res.data;
313
+ }
314
+ return res.data;
315
+ }
316
+ });
317
+ }
318
+ }
@@ -0,0 +1,74 @@
1
+ import { DeviceTypeGateway } from "./device_type_gateway";
2
+ import { deviceFilter } from "./device_filter";
3
+ import type { FeedTower, ElectricMeter } from ".";
4
+ import { FeedTowerGateway } from "./device_types/feedtower";
5
+ import { ElectricMeterGateway } from "./device_types/electricmeter";
6
+ import { NormalGateway } from "./device_types/normal_type";
7
+
8
+ //获得设备数据的网关,所有设备类型的数据都通过这个网关获取, 常见的设备直接通过属性访问,特殊的设备通过子属性访问
9
+ //例如
10
+ // shzx.device.feedTower: 料塔设备的数据获取网关
11
+ // shzx.device.electricMeter:电表设备的数据获取网关
12
+ export class DeviceGateway extends Object {
13
+ // private devices: Device[]
14
+ public type: DeviceTypeGateway;
15
+ filters: Map<string, deviceFilter<any>>;
16
+ normalFilter: NormalGateway;
17
+ context: Shzx;
18
+
19
+ constructor(context: Shzx) {
20
+ super();
21
+ this.context = context;
22
+ // this.devices = []
23
+ this.type = new DeviceTypeGateway(context);
24
+ this.normalFilter = new NormalGateway(context);
25
+ this.filters = new Map();
26
+ }
27
+
28
+ //料塔设备的数据获取网关
29
+ get feedTower(): deviceFilter<FeedTower> {
30
+ return this.getFilter("FeedTower", FeedTowerGateway);
31
+ }
32
+
33
+ //电表设备的数据获取网关
34
+ get electricMeter(): deviceFilter<ElectricMeter> {
35
+ return this.getFilter("ElectricMeter", ElectricMeterGateway);
36
+ }
37
+
38
+ private getFilter<T extends { id: number }>(
39
+ type: string,
40
+ cls: new (context: Shzx) => deviceFilter<T>
41
+ ): deviceFilter<T> {
42
+ if (this.filters.has(type)) {
43
+ return this.filters.get(type) as deviceFilter<T>;
44
+ }
45
+ const filter = new cls(this.context);
46
+ this.filters.set(type, filter);
47
+ return filter;
48
+ }
49
+
50
+ // addDevice(device: Device) {
51
+ // this.devices.push(device)
52
+ // }
53
+
54
+ // removeDevice(device: Device) {
55
+ // const index = this.devices.indexOf(device)
56
+ // if (index !== -1) {
57
+ // this.devices.splice(index, 1)
58
+ // }
59
+ // }
60
+
61
+ // getDevices() {
62
+ // return this.devices
63
+ // }
64
+
65
+ //获取设备列表,注意特殊类型,比如electricMeter和feedTower,需要通过属性访问
66
+ get list() {
67
+ return this.normalFilter.list;
68
+ }
69
+
70
+ get getHistoryDatas() {
71
+ return this.normalFilter.getHistoryDatas;
72
+ }
73
+
74
+ }
@@ -0,0 +1,27 @@
1
+ //缓存设备类型数据
2
+ export class DeviceTypeGateway {
3
+ deviceTypes: any
4
+ // axios: MyAxiosInstance;
5
+ context: Shzx
6
+
7
+ constructor(context: Shzx) {
8
+ this.context = context
9
+ }
10
+
11
+ list(): Promise<[]> {
12
+ if (this.deviceTypes) {
13
+ return Promise.resolve(this.deviceTypes)
14
+ }
15
+ return this.context.axios.get('/api/device_type/list')
16
+ }
17
+
18
+ alarmConfigForUnit(unit: Unit): Promise<any> {
19
+ return this.context.ready.then((axios) => {
20
+ return axios.get(`/api/v1/device/type/alarm`, {
21
+ params: {
22
+ unit_type: unit.type
23
+ }
24
+ })
25
+ });
26
+ }
27
+ }
@@ -0,0 +1,22 @@
1
+ import { type ElectricMeter } from "..";
2
+ import { deviceFilter } from "../device_filter";
3
+
4
+ export class ElectricMeterGateway extends deviceFilter<ElectricMeter> {
5
+ // Add properties and methods specific to the FeedTower type here
6
+
7
+ get deviceType() {
8
+ return "Electric";
9
+ }
10
+
11
+ //获取能耗关系
12
+ getEnergyRelation() {
13
+ return this.context.ready.then(() => {
14
+ return this.context.axios.get("/api/v1/energy/flow ").then((res) => {
15
+ if (res.data.code != 200) {
16
+ throw res.data.message;
17
+ }
18
+ return res.data;
19
+ });
20
+ });
21
+ }
22
+ }
@@ -0,0 +1,92 @@
1
+ import { type FeedTower } from '..'
2
+ import { deviceFilter, type DataQueryArgs } from '../device_filter'
3
+
4
+ export class FeedTowerGateway extends deviceFilter<FeedTower> {
5
+ // Add properties and methods specific to the FeedTower type here
6
+
7
+ get deviceType() {
8
+ return 'FeedTower'
9
+ }
10
+
11
+ getDetail(info: FeedTower, id: number) {
12
+ return this.context.axios
13
+ .post('/api/v1/pigfarm/device/status/detail?deviceId=' + id)
14
+ .then((res: any) => {
15
+ if (res.data.code != 200) {
16
+ throw res.data.message
17
+ }
18
+ res.data.data.unit = info.unit
19
+ return res.data.data
20
+ })
21
+ }
22
+
23
+ wrapData(id: number, args: DataQueryArgs, p1: Promise<any>) {
24
+ const ps = [p1]
25
+ if (!args.ingoreInput) {
26
+ ps.push(
27
+ this.context.axios.post(`/api/v1/pigfarm/device/fodder`, {
28
+ device: id,
29
+ times: [args.start, args.stop]
30
+ })
31
+ )
32
+ }
33
+
34
+ return Promise.all(ps).then((res) => {
35
+ if (res[0].data.Error) {
36
+ throw res[0].data.Error
37
+ } else {
38
+ if (!args.ingoreInput) {
39
+ res[1].data.data.list.forEach((item: any) => {
40
+ res[0].data[item.date] += item.data
41
+ })
42
+ }
43
+ return res[0].data
44
+ }
45
+ })
46
+ }
47
+
48
+ wrapList(list: FeedTower[]): Promise<FeedTower[]> {
49
+ const ids = this.devices.map((device) => device.id)
50
+ //TODO 其他设备的详情需要支持,目前只有喂料塔
51
+ return this.context.axios
52
+ .post('/api/v1/pigfarm/device/status/list', ids)
53
+ .then((res) => {
54
+ if (res.data.code != 200) {
55
+ throw res.data.message
56
+ } else {
57
+ res.data.data.forEach((device: FeedTowerExtraInfo) => {
58
+ const found = list.find((d) => d.id === device.id)
59
+ if (found) {
60
+ Object.assign(found, device)
61
+ }
62
+ })
63
+ return list
64
+ }
65
+ })
66
+ }
67
+
68
+
69
+ async enter(data: FodderAdd): Promise<any> {
70
+ const res = await this.context.axios.post('/api/v1/pigfarm/device/fodder/import', data)
71
+ return res
72
+ }
73
+ async getCar(truck: string): Promise<any> {
74
+ const res = await this.context.axios.get('/api/v1/pigfarm/car', {
75
+ params: {
76
+ truckCode: truck,
77
+ random: 1
78
+ }
79
+ })
80
+ return res.data
81
+ }
82
+ async getCarList(): Promise<any> {
83
+ const res = await this.context.axios.get('/api/v1/pigfarm/car/list')
84
+ return res.data
85
+ }
86
+ async updateZeroTime(id: Number): Promise<any> {
87
+ const res = await this.context.axios.post(
88
+ '/api/v1/pigfarm/device/status/updateZeroTime' + '?id=' + id
89
+ )
90
+ return res
91
+ }
92
+ }
@@ -0,0 +1,8 @@
1
+ import { deviceFilter } from "../device_filter";
2
+
3
+ //适配常见设备类型
4
+ export class NormalGateway extends deviceFilter<Device> {
5
+ get deviceType() {
6
+ return null;
7
+ }
8
+ }
@@ -0,0 +1,3 @@
1
+ # 分类型的设备获取类
2
+
3
+ ## 最简易模型参考[这里](./normal.ts)
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AssistantGateway = void 0;
4
+ //消息助手网关,获取消息助手的信息
5
+ class AssistantGateway extends Object {
6
+ constructor(context) {
7
+ super();
8
+ this.context = context;
9
+ }
10
+ getByLoginUser() {
11
+ return this.context.ready.then((axios) => {
12
+ return axios.get('/api/v1/robot/wx').then((res) => {
13
+ if (res.data.Error) {
14
+ throw res.data.Error;
15
+ }
16
+ return res.data;
17
+ });
18
+ });
19
+ }
20
+ }
21
+ exports.AssistantGateway = AssistantGateway;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CameraGateway = void 0;
4
+ class CameraGateway extends Object {
5
+ constructor(context) {
6
+ super();
7
+ this.context = context;
8
+ }
9
+ //搜索摄像头
10
+ async search(data) {
11
+ await this.context.ready;
12
+ const response = await this.context.axios.post('/api/v1/camera/search', {
13
+ unit_id: data.id
14
+ });
15
+ return response.data;
16
+ }
17
+ //获得摄像头的播放地址
18
+ async getUrl(data) {
19
+ await this.context.ready;
20
+ const response = await this.context.axios.get(`/api/v1/camera/${data.id}/url/wss`);
21
+ return response.data;
22
+ }
23
+ //获取工厂天气
24
+ async weather(id) {
25
+ await this.context.ready;
26
+ const response = await this.context.axios.get(`/api/v1/weather?code=${id}`);
27
+ return response.data;
28
+ }
29
+ }
30
+ exports.CameraGateway = CameraGateway;