Skip to content

Commit 3f24786

Browse files
authored
feat: adding smart building example (#46)
1 parent fa7c294 commit 3f24786

50 files changed

Lines changed: 3520 additions & 2 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/docker.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
strategy:
1616
fail-fast: false
1717
matrix:
18-
target: [ diningPhilosophers, count, cigaretteSmokers, chameneos, big, sleepingBarber, pingPong ]
18+
target: [ diningPhilosophers, count, cigaretteSmokers, chameneos, big, sleepingBarber, pingPong, smartBuilding ]
1919
permissions:
2020
packages: write
2121
contents: read

settings.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,6 @@ include("diningPhilosophers")
1212

1313
include("sleepingBarber")
1414

15-
include("pingPong")
15+
include("pingPong")
16+
17+
include("smartBuilding")

smartBuilding.Dockerfile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM gradle:9.3.0-jdk25 AS build
2+
3+
COPY --chown=gradle:gradle . /usr/src/cirrina-baselines
4+
5+
WORKDIR /usr/src/cirrina-baselines
6+
7+
RUN gradle :smartBuilding:distZip
8+
9+
RUN unzip smartBuilding/build/distributions/smartBuilding.zip -d /tmp
10+
11+
FROM gcr.io/distroless/java25-debian13 AS runtime
12+
13+
COPY --from=build /tmp/smartBuilding /opt/smartBuilding
14+
15+
ENTRYPOINT ["java", "-cp", "/opt/smartBuilding/lib/*", "ac.at.uibk.dps.dapr.bms.BMSKt"]

smartBuilding/build.gradle.kts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
plugins { id("common-conventions") }
2+
3+
dependencies {
4+
implementation("org.apache.fory:fory-core:0.15.0")
5+
implementation("org.apache.fory:fory-kotlin:0.15.0")
6+
}
7+
8+
application { mainClass.set("ac.at.uibk.dps.dapr.bms.BMSKt") }
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package ac.at.uibk.dps.dapr.bms
2+
3+
import ac.at.uibk.dps.dapr.bms.accessControl.AccessControlActorImpl
4+
import ac.at.uibk.dps.dapr.bms.buildingSchedule.BuildingScheduleActorImpl
5+
import ac.at.uibk.dps.dapr.bms.electricalsafety.ElectricalSafetyActorImpl
6+
import ac.at.uibk.dps.dapr.bms.energyManagement.EnergyManagementActorImpl
7+
import ac.at.uibk.dps.dapr.bms.fire.FireActorImpl
8+
import ac.at.uibk.dps.dapr.bms.firedoor.FireDoorActorImpl
9+
import ac.at.uibk.dps.dapr.bms.gasSafety.GasSafetyActorImpl
10+
import ac.at.uibk.dps.dapr.bms.hvac.HvacActorImpl
11+
import ac.at.uibk.dps.dapr.bms.lighting.LightingActorImpl
12+
import ac.at.uibk.dps.dapr.bms.roomSchedule.RoomScheduleActorImpl
13+
import ac.at.uibk.dps.dapr.bms.roomoccupancy.RoomOccupancyActorImpl
14+
import ac.at.uibk.dps.dapr.bms.securityManager.SecurityManagerActorImpl
15+
import ac.at.uibk.dps.dapr.bms.shading.ShadingActorImpl
16+
import ac.at.uibk.dps.dapr.bms.tempSafety.TempSafetyActorImpl
17+
import io.dapr.actors.runtime.ActorRuntime
18+
import org.springframework.boot.autoconfigure.SpringBootApplication
19+
import org.springframework.boot.runApplication
20+
21+
@SpringBootApplication class BMS
22+
23+
fun main(args: Array<String>) {
24+
val role = System.getenv("ROLE") ?: "lighting"
25+
when (role) {
26+
"lighting" -> ActorRuntime.getInstance().registerActor(LightingActorImpl::class.java)
27+
"hvac" -> ActorRuntime.getInstance().registerActor(HvacActorImpl::class.java)
28+
"shading" -> ActorRuntime.getInstance().registerActor(ShadingActorImpl::class.java)
29+
"roomOccupancy" -> ActorRuntime.getInstance().registerActor(RoomOccupancyActorImpl::class.java)
30+
"fire" -> ActorRuntime.getInstance().registerActor(FireActorImpl::class.java)
31+
"fireDoor" -> ActorRuntime.getInstance().registerActor(FireDoorActorImpl::class.java)
32+
"electricalSafety" ->
33+
ActorRuntime.getInstance().registerActor(ElectricalSafetyActorImpl::class.java)
34+
"gasSafety" -> ActorRuntime.getInstance().registerActor(GasSafetyActorImpl::class.java)
35+
"tempSafety" -> ActorRuntime.getInstance().registerActor(TempSafetyActorImpl::class.java)
36+
"buildingSchedule" ->
37+
ActorRuntime.getInstance().registerActor(BuildingScheduleActorImpl::class.java)
38+
"roomSchedule" -> ActorRuntime.getInstance().registerActor(RoomScheduleActorImpl::class.java)
39+
"energyManagement" ->
40+
ActorRuntime.getInstance().registerActor(EnergyManagementActorImpl::class.java)
41+
"securityManager" ->
42+
ActorRuntime.getInstance().registerActor(SecurityManagerActorImpl::class.java)
43+
"accessControl" -> ActorRuntime.getInstance().registerActor(AccessControlActorImpl::class.java)
44+
}
45+
runApplication<BMS>(*args)
46+
}
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
package ac.at.uibk.dps.dapr.bms
2+
3+
import ac.at.uibk.dps.dapr.bms.bindings.*
4+
import java.net.URI
5+
import java.net.http.HttpClient
6+
import java.net.http.HttpRequest
7+
import java.net.http.HttpResponse
8+
import org.apache.fory.Fory
9+
import org.apache.fory.ThreadSafeFory
10+
import org.apache.fory.config.Language
11+
import org.apache.fory.memory.MemoryBuffer
12+
13+
class BuildingServiceClient(private val baseUrl: String = "http://localhost:8005") {
14+
15+
private val client = HttpClient.newHttpClient()
16+
17+
companion object {
18+
private val fory: ThreadSafeFory =
19+
Fory.builder()
20+
.withLanguage(Language.XLANG)
21+
.withRefTracking(true)
22+
.buildThreadSafeFory()
23+
.apply {
24+
listOf(
25+
EmptyRequest::class.java,
26+
RoomRequest::class.java,
27+
LightLevelRequest::class.java,
28+
EvacuationLightsRequest::class.java,
29+
HvacRequest::class.java,
30+
IndoorTempResponse::class.java,
31+
BlindLevelRequest::class.java,
32+
OutdoorTempResponse::class.java,
33+
OccupancyRequest::class.java,
34+
OccupancyResponse::class.java,
35+
FireDetectionRequest::class.java,
36+
FireDetectionResponse::class.java,
37+
FireDoorRequest::class.java,
38+
ArcFaultResponse::class.java,
39+
TripCircuitBreakerRequest::class.java,
40+
GasLeakResponse::class.java,
41+
GasValveRequest::class.java,
42+
RoomTempResponse::class.java,
43+
ScheduleModeResponse::class.java,
44+
EnergyPriceResponse::class.java,
45+
GridStatusResponse::class.java,
46+
SecurityNotificationRequest::class.java,
47+
DoorRouteResponse::class.java,
48+
AuthRequest::class.java,
49+
AuthResponse::class.java,
50+
AccessRuleRequest::class.java,
51+
AccessDecisionResponse::class.java,
52+
DoorLockRequest::class.java,
53+
)
54+
.forEach { register(it, it.simpleName) }
55+
}
56+
57+
private val threadBuffer = ThreadLocal.withInitial { MemoryBuffer.newHeapBuffer(1024) }
58+
}
59+
60+
private fun action(path: String, req: Any = EmptyRequest()) = execute<Unit>(path, req, null, null)
61+
62+
private inline fun <reified T> query(
63+
path: String,
64+
req: Any = EmptyRequest(),
65+
noinline cb: (T) -> Unit,
66+
) = execute(path, req, T::class.java, cb)
67+
68+
private fun <T> execute(path: String, req: Any, resClass: Class<T>?, cb: ((T) -> Unit)?) {
69+
try {
70+
val buffer = threadBuffer.get().apply { writerIndex(0) }
71+
fory.serialize(buffer, req)
72+
73+
val request =
74+
HttpRequest.newBuilder()
75+
.uri(URI.create("$baseUrl$path"))
76+
.header("Content-Type", "application/x-fury")
77+
.POST(HttpRequest.BodyPublishers.ofByteArray(buffer.getBytes(0, buffer.writerIndex())))
78+
.build()
79+
80+
client.sendAsync(request, HttpResponse.BodyHandlers.ofByteArray()).thenAccept { res ->
81+
if (res.statusCode() in 200..299) {
82+
if (cb != null && resClass != null) {
83+
@Suppress("UNCHECKED_CAST") cb(fory.deserialize(res.body()) as T)
84+
}
85+
} else {
86+
println(" [SERVICE ERROR] $path -> Status: ${res.statusCode()}")
87+
}
88+
}
89+
} catch (e: Exception) {
90+
println(" [SERVICE ERROR] $path: ${e.message}")
91+
}
92+
}
93+
94+
// Lighting
95+
fun turnOn(roomId: String) = action("/turnOn", RoomRequest(roomId))
96+
97+
fun turnOff(roomId: String) = action("/turnOff", RoomRequest(roomId))
98+
99+
fun dim(roomId: String) = action("/dim", RoomRequest(roomId))
100+
101+
fun turnUserLevel(roomId: String, level: Int) =
102+
action("/userLevelLight", LightLevelRequest(roomId, level))
103+
104+
fun evacuationLights(roomId: String, emergencyId: String) =
105+
action("/evacuationLights", EvacuationLightsRequest(roomId, emergencyId))
106+
107+
// HVAC
108+
fun setHvac(mode: String, roomId: String) = action("/setHVAC", HvacRequest(mode, roomId))
109+
110+
fun getIndoorTemp(roomId: String, cb: (Double) -> Unit) =
111+
query<IndoorTempResponse>("/getIndoorTemp", RoomRequest(roomId)) { cb(it.indoorTemp) }
112+
113+
// Shading
114+
fun blindsHalf(roomId: String) = action("/blindsHalf", RoomRequest(roomId))
115+
116+
fun blindsOpen(roomId: String) = action("/blindsOpen", RoomRequest(roomId))
117+
118+
fun blindsClose(roomId: String) = action("/blindsClose", RoomRequest(roomId))
119+
120+
fun blindsUserLevel(roomId: String, level: Int) =
121+
action("/userLevelBlinds", BlindLevelRequest(roomId, level))
122+
123+
fun getOutdoorTemp(cb: (Double) -> Unit) =
124+
query<OutdoorTempResponse>("/getOutdoorTemp") { cb(it.outdoorTemp) }
125+
126+
// Occupancy
127+
fun detectOccupancy(imageData: String, cb: (Boolean) -> Unit) =
128+
query<OccupancyResponse>("/detectOccupancy", OccupancyRequest(imageData)) {
129+
cb(it.occupancyDetected)
130+
}
131+
132+
fun maintenance(roomId: String) = action("/maintenance", RoomRequest(roomId))
133+
134+
// Fire Safety
135+
fun detectFire(img: String, zone: String, cb: (String, String) -> Unit) =
136+
query<FireDetectionResponse>("/detectFire", FireDetectionRequest(img, zone)) {
137+
cb(it.fireDetectionResult, it.emergencyInRoom)
138+
}
139+
140+
fun openFireDoor(doorId: String) = action("/openFireDoor", FireDoorRequest(doorId))
141+
142+
fun closeFireDoor(doorId: String) = action("/closeFireDoor", FireDoorRequest(doorId))
143+
144+
// Electrical
145+
fun checkArcFault(cb: (String) -> Unit) =
146+
query<ArcFaultResponse>("/checkArcFault") { cb(it.arcFaultLocation) }
147+
148+
fun tripCircuitBreaker(loc: String) =
149+
action("/tripCircuitBreaker", TripCircuitBreakerRequest(loc))
150+
151+
fun acknowledgedElectrical(cb: () -> Unit) = action("/electricalFaultAcknowledged")
152+
153+
// Gas Safety
154+
fun checkGasLeak(cb: (String) -> Unit) =
155+
query<GasLeakResponse>("/checkGasFault") { cb(it.gasLeakLocation) }
156+
157+
fun closeGasValve(loc: String) = action("/closeGasValve", GasValveRequest(loc))
158+
159+
fun cutPower(loc: String) = action("/cutPower", GasValveRequest(loc))
160+
161+
fun gasLeakPurged() = action("/gasLeakPurged")
162+
163+
// Temp Safety
164+
fun getRoomTemp(roomId: String, cb: (Double) -> Unit) =
165+
query<RoomTempResponse>("/getRoomTemp", RoomRequest(roomId)) { cb(it.roomTemp) }
166+
167+
fun highRiskTemp(roomId: String) = action("/highRiskTemp", RoomRequest(roomId))
168+
169+
// Schedules
170+
fun getScheduleMode(cb: (String) -> Unit) =
171+
query<ScheduleModeResponse>("/getScheduleMode") { cb(it.currentSchedule) }
172+
173+
fun initializeZone(cb: () -> Unit) = action("/initializeZone")
174+
175+
// Energy
176+
fun getEnergyPrice(cb: (Double) -> Unit) =
177+
query<EnergyPriceResponse>("/getEnergyPrice") { cb(it.energyPrice) }
178+
179+
fun checkGridStatus(cb: (String) -> Unit) =
180+
query<GridStatusResponse>("/checkGridStatus") { cb(it.gridStatus) }
181+
182+
// Security & Access
183+
fun notifySecurity(msg: String) = action("/notifySecurity", SecurityNotificationRequest(msg))
184+
185+
fun getDoorRouteType(doorId: String, cb: (Boolean, String) -> Unit) =
186+
query<DoorRouteResponse>("/getDoorRouteType", FireDoorRequest(doorId)) {
187+
cb(it.isEvacuationRoute, it.zoneId)
188+
}
189+
190+
fun authenticateUser(cardId: String, cb: (String, String, String) -> Unit) =
191+
query<AuthResponse>("/authenticateUser", AuthRequest(cardId)) {
192+
cb(it.userId, it.userRole, it.authenticationStatus)
193+
}
194+
195+
fun checkAccessRule(role: String, zone: String, mode: String, cb: (String) -> Unit) =
196+
query<AccessDecisionResponse>("/checkAccessRule", AccessRuleRequest(role, zone, mode)) {
197+
cb(it.accessDecision)
198+
}
199+
200+
fun controlDoorLock(doorId: String, cmd: String) =
201+
action("/controlDoorLock", DoorLockRequest(doorId, cmd))
202+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package ac.at.uibk.dps.dapr.bms.accessControl
2+
3+
import io.dapr.actors.ActorType
4+
5+
/** Access control actor interface. */
6+
@ActorType(name = "AccessControlActor")
7+
interface AccessControlActor {
8+
fun initialize()
9+
10+
fun onAuthenticationRequest(cardId: String)
11+
12+
fun onForceUnlockRequest()
13+
14+
fun onPhysicalTamper()
15+
16+
fun onDoorForcedOpen()
17+
18+
fun onLockdownZone()
19+
20+
fun onFireAlarm()
21+
22+
fun onGasLeakDetected()
23+
24+
fun onUnlockAllEvacuationRoutes()
25+
26+
fun onClearSecurityAlert()
27+
28+
fun onDisarmFireAlarm()
29+
30+
fun onGasPurged()
31+
32+
fun onEnterBusinessHours()
33+
34+
fun onEnterAfterHours()
35+
36+
fun onEnterWeekend()
37+
}

0 commit comments

Comments
 (0)