Skip to main content

Implementation details

Principles

Dependency Inversion Principle

The Dependency Inversion Principle (DIP) has been applied to the system to decouple the high-level modules from the low-level modules. This principle states that high-level modules should not depend on low-level modules, but both should depend on abstractions. Every module in the system depends on interfaces, to let the implementation be changed without affecting the other modules.

This type of principle has been applied to the entire system, from the message broker to the databases, to allow rapid changes or evolutions.

Example: AlarmEventsHub interface

export interface AlarmEventsHub {
publishAnomaly(anomaly: Anomaly): void

subscribeToMeasurements(handler: (measurement: Measurement) => void): void

subscribeToDetections(handler: (detection: Detection) => void): void

subscribeToDevices(handler: (deviceEvent: DeviceEvent) => void): void

addMeasurementTopics(topics: string[]): void

removeMeasurementTopics(topics: string[]): void
}

Or simply a Repository management interface:

Example: SecurityRuleRepository interface

export interface SecurityRuleRepository {
getSecurityRules(): Promise<SecurityRule[]>

getSecurityRuleById(id: SecurityRuleId): Promise<SecurityRule>

saveSecurityRule(securityRule: SecurityRule): Promise<void>

updateSecurityRule(securityRule: SecurityRule): Promise<void>

removeSecurityRule(securityRuleId: SecurityRuleId): Promise<void>

getRangeRules(): Promise<RangeRule[]>

getIntrusionRules(): Promise<IntrusionRule[]>

enableSecurityRule(securityRuleId: SecurityRuleId): Promise<void>

disableSecurityRule(securityRuleId: SecurityRuleId): Promise<void>
}

These interfaces are implemented by the classes that need to use them, and they can be changed without affecting the core modules.

Technologies

JSON Web Token

The Auth service is responsible for the authentication of the user and the generation of the Json Web Token (JWT) token that will be used to authenticate the user in the other services. The generated token can be validated from each microservice to ensure that the user is authenticated. Following this approach, in case of failure of the authentication service, the user can still access the system until its token validity expires.

Kafka

Apache Kafka is the technology chosen to handle intra-system real-time communications. In particular, KafkaJS and kafka-python clients have been used respectively for the Node.js and Flask services. Kafka is an open-source distributed event streaming platform capable of handling plenty of events per second.

Example: Kafka consumer

this.consumer
.startConsuming(topics, false, (message: KafkaMessage): void => {
// Message arrived!
})
.then((): void => console.log('Consumer started'))

Example:python Kafka producer

def publish_detection(self, camera: str, object_class: str):
detection: Detection = DetectionFactory.create_detection(
datetime.now(), camera_code, object_class
)


detection_to_publish = transform_keys(
json.loads(detection.model_dump_json()), snake_to_camel
)
self._producer.produce("detections." + camera_code, detection_to_publish)

Socket.io

Socket IO is a library that enables real-time, bidirectional and event-based communication between the browser and the server. It has been exploited to implement the real-time communication between the user and the system, in particular for pushing notifications or sending the measurements to the user. A security layer has been added to socket servers to ensure secure connections. Also for this feature, the JWT token generated when the user logs in has been used.

io.use(function (socket, next): void {
if (socket.handshake.query && socket.handshake.query.token) {
if (jwtManager.verify(socket.handshake.query.token as string)) next()
} else {
//Authentication error
}
})

Object Recognition with YOLO

You Only Look Once (YOLO) is a state-of-the-art, efficient real-time object detection algorithm. YOLO can identify object classes and their positions within a video. This algorithm is used by the Recognition service to perform object detection on video streams generated by the devices, in accordance with active security rules.

Example: YOLO object detection

class RecognitionService(ABC):

@abstractmethod
def start_recognizing(self, camera_code: str) -> None:
"""
It starts to recognize the video stream produced by a camera.
If it is already recognizing the camera, it does nothing.
:param camera_code: the camera code of the camera to start recognizing
"""

@abstractmethod
def stop_recognizing(self, camera_code: str) -> None:
"""
It stops recognizing the video stream produced by a camera.
If it is not recognizing the camera, it does nothing.
:param camera_code: the camera code of the camera to stop recognizing
"""

@abstractmethod
def stop_all_recognizing(self):
"""
It stops recognizing the video stream produced by all cameras.
"""

Media Server

MediaMTX has been used to allow the system to support multiple protocols without needing a separate process for each one. All cameras connect to the media server, which handles video stream distribution across different protocols. In this configuration, the cameras use the RTSP protocol to send video streams, which are then consumed by the Recognition component. Simultaneously, the Frontend uses the WebRTC protocol to access the video streams.

WebRTC

The WebRTC protocol is used to simulate camera streams, chosen for its ability to stream video and audio in real-time with ease. It requires no plugins or software installation, is supported by all major browsers, and is open-source. Developed by Google, WebRTC implements the Google Congestion Control (GCC) algorithm, which enables real-time streaming of video and audio with low latency.