Skip to content

Commit 0b08c95

Browse files
authored
Merge pull request #240 from DevKor-github/feat/ble
BLE api 수정 및 db형식에 맞게 변경
2 parents a3a60a0 + f4f2f9c commit 0b08c95

8 files changed

Lines changed: 144 additions & 22 deletions

File tree

src/main/java/devkor/com/teamcback/domain/ble/controller/BLEController.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package devkor.com.teamcback.domain.ble.controller;
22

33
import devkor.com.teamcback.domain.ble.dto.request.UpdateBLEReq;
4+
import devkor.com.teamcback.domain.ble.dto.response.BLETimePatternRes;
45
import devkor.com.teamcback.domain.ble.dto.response.GetBLERes;
56
import devkor.com.teamcback.domain.ble.dto.response.UpdateBLERes;
67
import devkor.com.teamcback.domain.ble.service.BLEService;
@@ -41,8 +42,25 @@ public CommonResponse<GetBLERes> getBLE(
4142
}
4243

4344
@PutMapping
45+
@Operation(summary = "ble 기기에서 전송하는 데이터 통한 데이터 축적",
46+
description = "ble 기기에서 전송하는 데이터 통한 데이터 축적")
4447
public CommonResponse<UpdateBLERes> updateBLE(
4548
@Valid @RequestBody UpdateBLEReq updateBLEReq){
4649
return CommonResponse.success(bleService.updateBLE(updateBLEReq));
4750
}
51+
52+
@GetMapping("/pattern")
53+
@Operation(summary = "BLE 요일/시간대별 평균 인원 조회",
54+
description = "최근 1달 동안 특정 placeId에 대해 요일별 7/10/13/16/19/22시 기준 평균 인원 수를 반환")
55+
@ApiResponses(value = {
56+
@ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."),
57+
@ApiResponse(responseCode = "404", description = "장소 또는 장비를 찾을 수 없습니다.",
58+
content = @Content(schema = @Schema(implementation = CommonResponse.class))),
59+
})
60+
public CommonResponse<BLETimePatternRes> getBLETimePattern(
61+
@Parameter(name="placeId", description = "BLE 정보를 얻고자 하는 placeId")
62+
@RequestParam Long placeId) {
63+
return CommonResponse.success(bleService.getBLETimePattern(placeId));
64+
}
65+
4866
}

src/main/java/devkor/com/teamcback/domain/ble/dto/request/UpdateBLEReq.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
@Getter
1313
@Setter
1414
public class UpdateBLEReq {
15-
@Schema(description = "라운지 설치된 기기명", example = "woodang_1f_lounge")
15+
@Schema(description = "라운지 설치된 기기명", example = "SKFutureHall_5f_lounge_517")
1616
private String deviceName;
17-
@Schema(description = "최근 감지 인원", example = "10")
17+
@Schema(description = "최근 감지 신호 개수", example = "10")
1818
private int lastCount;
1919
@Schema(description = "최근 신호 전송 시간")
2020
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package devkor.com.teamcback.domain.ble.dto.response;
2+
3+
import lombok.Getter;
4+
5+
@Getter
6+
public class BLETimePatternRes {
7+
private Long placeId; // 요청 placeId
8+
private int[] hours; // {7,10,13,16,19,21,24}
9+
private int[] dayOfWeeks; // {1,2,3,4,5,6,7} (java.time.DayOfWeek 값)
10+
private int[][] averages; // [dayIndex][timeIndex] 형태, 각 원소는 반올림된 int
11+
12+
public BLETimePatternRes(Long placeId, int[] timeSlots, int[] dayOfWeeks, int[][] averages) {
13+
this.placeId = placeId;
14+
this.hours = timeSlots;
15+
this.dayOfWeeks = dayOfWeeks;
16+
this.averages = averages;
17+
}
18+
}

src/main/java/devkor/com/teamcback/domain/ble/dto/response/GetBLERes.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ public class GetBLERes {
1717
private int lastStatus;
1818
private LocalDateTime lastTime;
1919

20-
public GetBLERes(BLEDevice device, BLEData data, BLEstatus status) {
20+
public GetBLERes(BLEDevice device, BLEData data, BLEstatus status, int people) {
2121
this.id = device.getId();
2222
this.deviceName = device.getDeviceName();
2323
this.placeId = device.getPlace().getId();
2424
this.capacity = device.getCapacity();
25-
this.lastCount = data.getLastCount();
25+
this.lastCount = people;
2626
this.lastStatus = status.getCode();
2727
this.lastTime = data.getLastTime();
2828
}

src/main/java/devkor/com/teamcback/domain/ble/dto/response/UpdateBLERes.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
public class UpdateBLERes {
1212
@Schema(description = "라운지 설치된 기기명", example = "woodang_1f_lounge")
1313
private Long deviceId;
14-
@Schema(description = "최근 감지 인원", example = "10")
14+
@Schema(description = "최근 감지 인원(10의 자리 반올림)", example = "10")
1515
private int lastCount;
1616
@Schema(description = "최근 Status", example = "AVAILABLE")
1717
private BLEstatus lastStatus;

src/main/java/devkor/com/teamcback/domain/ble/entity/BLEDevice.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ public class BLEDevice extends BaseEntity {
3131
private int capacity;
3232

3333
@Column(name="default_count")
34-
private int defaultCount;
34+
private double defaultCount;
3535

3636
@Column
37-
private int ratio;
37+
private double ratio;
3838

3939
public BLEDevice(CreateBLEDeviceReq req, Place place) {
4040
this.deviceName = req.getDeviceName();

src/main/java/devkor/com/teamcback/domain/ble/repository/BLEDataRepository.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
import org.springframework.data.jpa.repository.JpaRepository;
66
import org.springframework.data.repository.CrudRepository;
77

8+
import java.time.LocalDateTime;
9+
import java.util.List;
810
import java.util.Optional;
911

1012
public interface BLEDataRepository extends JpaRepository<BLEData, Long> {
1113
Optional<BLEData> findTopByDeviceOrderByLastTimeDesc(BLEDevice device);
14+
List<BLEData> findAllByDeviceAndLastTimeBetweenOrderByLastTimeAsc(BLEDevice device, LocalDateTime start, LocalDateTime end);
1215
}

src/main/java/devkor/com/teamcback/domain/ble/service/BLEService.java

Lines changed: 98 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33

44
import devkor.com.teamcback.domain.ble.dto.request.UpdateBLEReq;
5+
import devkor.com.teamcback.domain.ble.dto.response.BLETimePatternRes;
56
import devkor.com.teamcback.domain.ble.dto.response.GetBLERes;
67
import devkor.com.teamcback.domain.ble.dto.response.UpdateBLERes;
78
import devkor.com.teamcback.domain.ble.entity.BLEData;
@@ -32,10 +33,21 @@ public class BLEService {
3233
private final BLEDataRepository bleDataRepository;
3334
private final PlaceRepository placeRepository;
3435

36+
// 평균 구해올 시간대 라벨
37+
private static final int[] TIME_SLOTS = {7, 10, 13, 16, 19, 22};
38+
//요일 라벨 (1=월요일, 7=일요일)
39+
private static final int[] DAY_OF_WEEKS = {1, 2, 3, 4, 5, 6, 7};
40+
3541
@Transactional
3642
public UpdateBLERes updateBLE(UpdateBLEReq updateBLEReq) {
3743
BLEDevice bleDevice = bledeviceRepository.findByDeviceName(updateBLEReq.getDeviceName());
38-
BLEstatus status = getBlEstatus(updateBLEReq, bleDevice);
44+
int capacity = bleDevice.getCapacity();
45+
int people = getBlEPeople(updateBLEReq.getLastCount(), bleDevice);
46+
double final_ratio = (double) people / capacity;
47+
BLEstatus status;
48+
if (final_ratio < 0.3) status = BLEstatus.VACANT;
49+
else if (final_ratio < 0.7) status = BLEstatus.AVAILABLE;
50+
else status = BLEstatus.CROWDED;
3951

4052
BLEData bleData = new BLEData();
4153
bleData.setDevice(bleDevice);
@@ -47,21 +59,16 @@ public UpdateBLERes updateBLE(UpdateBLEReq updateBLEReq) {
4759
return new UpdateBLERes(bleData);
4860
}
4961

50-
private BLEstatus getBlEstatus(UpdateBLEReq updateBLEReq, BLEDevice bleDevice) {
62+
private int getBlEPeople(int lastCount, BLEDevice bleDevice) {
5163
if (bleDevice == null) throw new GlobalException(ResultCode.NOT_FOUND_DEVICE_NAME);
52-
int capacity = bleDevice.getCapacity();
53-
int count = updateBLEReq.getLastCount();
54-
int ratio = bleDevice.getRatio();
55-
int defaultCount = bleDevice.getDefaultCount();
56-
57-
double people_count = (double) (count - defaultCount) / ratio;
58-
double final_ratio = people_count / capacity;
64+
double ratio = bleDevice.getRatio();
65+
double defaultCount = bleDevice.getDefaultCount();
66+
return calculate_people(lastCount, ratio, defaultCount);
67+
}
5968

60-
BLEstatus status;
61-
if (final_ratio < 0.3) status = BLEstatus.VACANT;
62-
else if (final_ratio < 0.7) status = BLEstatus.AVAILABLE;
63-
else status = BLEstatus.CROWDED;
64-
return status;
69+
//count, ratio, defaultcount로 예측 인원 계산(int)
70+
private int calculate_people(int count, double ratio, double defaultCount) {
71+
return (int) Math.round(count * ratio + defaultCount);
6572
}
6673

6774
@Transactional(readOnly = true)
@@ -76,7 +83,83 @@ public GetBLERes getBLE(Long placeId) {
7683
status = BLEstatus.FAILURE;
7784
}
7885
else status = latest.getLastStatus();
79-
return new GetBLERes(device, latest, status);
86+
// 사람 수를 예측 후 10의 배수로 리턴
87+
int people = getBlEPeople(latest.getLastCount(), device);
88+
people = (int) Math.round(people / 10.0) * 10;
89+
return new GetBLERes(device, latest, status, people);
90+
}
91+
92+
@Transactional(readOnly = true)
93+
public BLETimePatternRes getBLETimePattern(Long placeId) {
94+
//place, device 조회
95+
Place place = placeRepository.findById(placeId).orElseThrow(() -> new GlobalException(NOT_FOUND_PLACE));
96+
BLEDevice device = bledeviceRepository.findByPlace(place);
97+
if (device == null) throw new GlobalException(ResultCode.NOT_FOUND_DEVICE);
98+
double ratio = device.getRatio();
99+
double defaultCount = device.getDefaultCount();
100+
//기간 설정: 1달
101+
LocalDateTime end = LocalDateTime.now();
102+
LocalDateTime start = end.minusMonths(1);
103+
//1달치 bledata 가져오기
104+
List<BLEData> dataList = bleDataRepository.findAllByDeviceAndLastTimeBetweenOrderByLastTimeAsc(device, start, end);
105+
//계산 위한 배열 준비
106+
// dayIndex: 0~6 (월~일), timeIndex: 0~6 (7,10,13,16,19,22)
107+
long[][] sum = new long[DAY_OF_WEEKS.length][TIME_SLOTS.length];
108+
long[][] count = new long[DAY_OF_WEEKS.length][TIME_SLOTS.length];
109+
// 시간대를 "가장 가까운 target 시간"으로 매핑하기 위해 분 단위로 처리
110+
int[] slotMinutes = new int[TIME_SLOTS.length];
111+
for (int i = 0; i < TIME_SLOTS.length; i++) {
112+
slotMinutes[i] = TIME_SLOTS[i] * 60;
113+
}
114+
// BLEData를 요일/시간대 bucket에 넣어서 합/개수 집계
115+
for (BLEData d : dataList) {
116+
if (d.getLastTime() == null) continue;
117+
if (d.getLastCount() == null) continue;
118+
LocalDateTime t = d.getLastTime();
119+
// 요일 index (0~6) : DayOfWeek.getValue() 는 1~7(MON~SUN)
120+
int dayValue = t.getDayOfWeek().getValue(); // 1~7
121+
int dayIndex = dayValue - 1;
122+
// 분 단위 시간 (0~1439)
123+
int minuteOfDay = t.getHour() * 60 + t.getMinute();
124+
// 이 데이터가 들어갈 slot 찾기 (전후 30분 이내만 허용)
125+
int bestSlotIndex = -1;
126+
int bestDiff = Integer.MAX_VALUE;
127+
128+
for (int i = 0; i < slotMinutes.length; i++) {
129+
int diff = Math.abs(minuteOfDay - slotMinutes[i]);
130+
131+
// 전후 30분(= 60분 window) 안에 들어오는 경우만 고려
132+
if (diff <= 30 && diff < bestDiff) {
133+
bestDiff = diff;
134+
bestSlotIndex = i;
135+
}
136+
}
137+
// 어떤 slot에도 해당되지 않으면 이 데이터는 버림
138+
if (bestSlotIndex == -1) {
139+
continue;
140+
}
141+
// 합/개수 축적
142+
sum[dayIndex][bestSlotIndex] += calculate_people(d.getLastCount(), ratio, defaultCount);
143+
count[dayIndex][bestSlotIndex] += 1;
144+
}
145+
// 평균 계산 (반올림 후 int)
146+
int[][] averages = new int[DAY_OF_WEEKS.length][TIME_SLOTS.length];
147+
for (int di = 0; di < DAY_OF_WEEKS.length; di++) {
148+
for (int ti = 0; ti < TIME_SLOTS.length; ti++) {
149+
if (count[di][ti] == 0L) {
150+
averages[di][ti] = 0; // 데이터 없으면 0
151+
} else {
152+
double avg = (double) sum[di][ti] / (double) count[di][ti];
153+
averages[di][ti] = (int) Math.round(avg);
154+
}
155+
}
156+
}
157+
return new BLETimePatternRes(
158+
placeId,
159+
TIME_SLOTS,
160+
DAY_OF_WEEKS,
161+
averages
162+
);
80163
}
81164

82165
}

0 commit comments

Comments
 (0)