Commit 64cec198 authored by areej.mohammad's avatar areej.mohammad

projct

parent 4a9e78b2
# Default ignored files
/shelf/
/workspace.xml
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="dashboard_java" />
</profile>
</annotationProcessing>
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="dashboard_java" options="-parameters" />
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/dashboard_java/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/dashboard_java/src/main/resources" charset="UTF-8" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/dashboard_java/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/weather_mulltilanguage_grpc.iml" filepath="$PROJECT_DIR$/.idea/weather_mulltilanguage_grpc.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
import grpc
import time
import sqlite3
import random
from concurrent import futures
# Correct direct imports (since files are next to analytics_server.py)
import weather_pb2 as pb2
import weather_pb2_grpc as pb2_grpc
# Expected secret key for authentication
VALID_API_TOKEN = "SECRET_WEATHER_KEY_2025"
REPORT_INTERVAL_SECONDS = 5
DATABASE_NAME = 'weather_data.db'
# --- 1. SQLite Database Setup (Optional requirement) ---
def init_db():
conn = sqlite3.connect(DATABASE_NAME)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS weather_stats (
id INTEGER PRIMARY KEY,
avg_temp REAL,
avg_humidity REAL,
avg_pressure REAL,
report_time INTEGER
)
""")
conn.commit()
conn.close()
# --- 2. Analytics Service Logic ---
class AnalyticsServicer(pb2_grpc.WeatherAnalyticsServiceServicer):
def __init__(self):
super().__init__()
# Variable to store the last sent stat (for Unary RPC)
self.last_stats = pb2.WeatherStats(avg_temperature=0, avg_humidity=0, avg_pressure=0)
# Queue list for Dashboard clients subscribers
self.dashboard_subscribers = []
# --- Helper function for Authentication ---
def _authenticate(self, context):
"""Checks for the correct authentication key in Metadata."""
# Get metadata from the context
metadata = dict(context.invocation_metadata())
# gRPC converts all keys to lowercase
auth_token = metadata.get('authorization')
# The token must be in the format: Bearer <TOKEN>
if auth_token and auth_token.startswith('Bearer '):
token = auth_token.split('Bearer ')[1]
if token == VALID_API_TOKEN:
print("✅ Authentication successful.")
return True
# If authentication fails, abort the connection with UNAUTHENTICATED error
print("❌ Authentication failed. Connection refused.")
context.abort(
grpc.StatusCode.UNAUTHENTICATED,
"Authentication failed. Invalid or missing API token."
)
return False
# A. Client Streaming: Receiving Data from Sensor
def StreamWeatherData(self, request_iterator, context):
# 1. Authenticate first
if not self._authenticate(context):
return
print(f"\n--- Starting to receive sensor data from: {context.peer()} ---")
# Variables for average calculation
temps, hums, press = [], [], []
count = 0
for data in request_iterator:
count += 1
temps.append(data.temperature_celsius)
hums.append(data.humidity_percent)
press.append(data.pressure_hpa)
# 2. Boundary Check and Status Error Trigger (Alert)
if data.temperature_celsius > 45:
# Abort the client stream with an error to signal an alert
context.abort(
grpc.StatusCode.RESOURCE_EXHAUSTED,
"ALERT: Extreme temperature detected! System overloading."
)
return self.last_stats # Must return something even if the connection fails
# 3. Calculate average and send to subscribers every 5 readings
if count % 5 == 0 and count > 0:
avg_stats = pb2.WeatherStats(
avg_temperature=sum(temps) / len(temps),
avg_humidity=sum(hums) / len(hums),
avg_pressure=sum(press) / len(press),
report_time=int(time.time()),
)
print(f" > Analysis: Temp Avg={avg_stats.avg_temperature:.2f} (Saved locally).")
self.save_report(avg_stats)
self.last_stats = avg_stats
# Send the report to all Dashboard subscribers
for subscriber_queue in self.dashboard_subscribers:
subscriber_queue.append(avg_stats)
# Reset variables
temps, hums, press = [], [], []
print("--- Sensor data stream finished ---")
# Return the last stat upon client stream completion
return self.last_stats
# B. Server Streaming: Sending Analysis to Dashboard
def StreamAnalytics(self, request, context):
# 1. Add client to the subscriber list
# We use a local list as a "queue" for each subscribed client
report_queue = []
self.dashboard_subscribers.append(report_queue)
print(f"🟢 New Dashboard client subscribed to analytics reports. Total subscribers: {len(self.dashboard_subscribers)}")
try:
# 2. Wait and Send loop
while True:
# If there are new reports in the queue, send them
if report_queue:
stats = report_queue.pop(0)
yield stats
else:
# Wait briefly to avoid resource consumption
time.sleep(1)
# If the client terminates the connection, exit the loop
if context.is_active() == False:
break
finally:
# 3. Remove client upon disconnection
if report_queue in self.dashboard_subscribers:
self.dashboard_subscribers.remove(report_queue)
print(f"🔴 Dashboard client disconnected. Total subscribers: {len(self.dashboard_subscribers)}")
# C. Unary RPC: Retrieve Last Stored Report (Optional Requirement)
def GetLastReport(self, request, context):
print(f"🔵 Last report requested manually by: {context.peer()}")
# Read from the database
conn = sqlite3.connect(DATABASE_NAME)
cursor = conn.cursor()
cursor.execute("SELECT avg_temp, avg_humidity, avg_pressure, report_time FROM weather_stats ORDER BY id DESC LIMIT 1")
row = cursor.fetchone()
conn.close()
if row:
return pb2.WeatherStats(
avg_temperature=row[0],
avg_humidity=row[1],
avg_pressure=row[2],
report_time=row[3]
)
else:
# If no data is available, return an appropriate error
context.abort(grpc.StatusCode.NOT_FOUND, "No reports available in the database.")
# --- Helper function for Data Storage (SQLite) ---
def save_report(self, stats):
conn = sqlite3.connect(DATABASE_NAME)
cursor = conn.cursor()
cursor.execute(
"INSERT INTO weather_stats (avg_temp, avg_humidity, avg_pressure, report_time) VALUES (?, ?, ?, ?)",
(stats.avg_temperature, stats.avg_humidity, stats.avg_pressure, stats.report_time)
)
conn.commit()
conn.close()
# --- 3. Run the Server ---
def serve():
init_db() # Initialize the database before starting
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
# Bind the service implementation to the server
pb2_grpc.add_WeatherAnalyticsServiceServicer_to_server(
AnalyticsServicer(), server
)
# Run the server on the specified port (50051)
port = '50051'
server.add_insecure_port(f'[::]:{port}')
server.start()
print(f"✨ Analytics Server (Python) running on port {port}")
# Keep the server running
try:
while True:
time.sleep(86400) # Sleep for a day
except KeyboardInterrupt:
server.stop(0)
print("Analytics Server stopped.")
if __name__ == '__main__':
# Run the server
serve()
\ No newline at end of file
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# NO CHECKED-IN PROTOBUF GENCODE
# source: weather.proto
# Protobuf Python Version: 6.31.1
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import runtime_version as _runtime_version
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
_runtime_version.ValidateProtobufRuntimeVersion(
_runtime_version.Domain.PUBLIC,
6,
31,
1,
'',
'weather.proto'
)
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rweather.proto\x12\x0eweather_system\"\x81\x01\n\x0bWeatherData\x12\x1b\n\x13temperature_celsius\x18\x01 \x01(\x01\x12\x18\n\x10humidity_percent\x18\x02 \x01(\x01\x12\x14\n\x0cpressure_hpa\x18\x03 \x01(\x01\x12\x11\n\ttimestamp\x18\x04 \x01(\x03\x12\x12\n\nstation_id\x18\x05 \x01(\t\"\x9d\x01\n\x0cWeatherStats\x12\x17\n\x0f\x61vg_temperature\x18\x01 \x01(\x01\x12\x14\n\x0c\x61vg_humidity\x18\x02 \x01(\x01\x12\x14\n\x0c\x61vg_pressure\x18\x03 \x01(\x01\x12\x13\n\x0breport_time\x18\x04 \x01(\x03\x12\x33\n\rcurrent_alert\x18\x05 \x01(\x0b\x32\x1c.weather_system.AlertMessage\"\xd8\x01\n\x0c\x41lertMessage\x12\x34\n\x04type\x18\x01 \x01(\x0e\x32&.weather_system.AlertMessage.AlertType\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\x01\x12\x11\n\ttimestamp\x18\x04 \x01(\x03\"_\n\tAlertType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x14\n\x10HIGH_TEMPERATURE\x10\x01\x12\x10\n\x0cLOW_PRESSURE\x10\x02\x12\x11\n\rHIGH_HUMIDITY\x10\x03\x12\n\n\x06NORMAL\x10\x04\"!\n\x0c\x41uthMetadata\x12\x11\n\tapi_token\x18\x01 \x01(\t\"\x07\n\x05\x45mpty2\xcb\x02\n\x17WeatherAnalyticsService\x12P\n\x11StreamWeatherData\x12\x1b.weather_system.WeatherData\x1a\x1c.weather_system.WeatherStats(\x01\x12H\n\x0fStreamAnalytics\x12\x15.weather_system.Empty\x1a\x1c.weather_system.WeatherStats0\x01\x12\x44\n\rGetLastReport\x12\x15.weather_system.Empty\x1a\x1c.weather_system.WeatherStats\x12N\n\rControlSensor\x12\x1c.weather_system.AlertMessage\x1a\x1b.weather_system.WeatherData(\x01\x30\x01\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'weather_pb2', _globals)
if not _descriptor._USE_C_DESCRIPTORS:
DESCRIPTOR._loaded_options = None
_globals['_WEATHERDATA']._serialized_start=34
_globals['_WEATHERDATA']._serialized_end=163
_globals['_WEATHERSTATS']._serialized_start=166
_globals['_WEATHERSTATS']._serialized_end=323
_globals['_ALERTMESSAGE']._serialized_start=326
_globals['_ALERTMESSAGE']._serialized_end=542
_globals['_ALERTMESSAGE_ALERTTYPE']._serialized_start=447
_globals['_ALERTMESSAGE_ALERTTYPE']._serialized_end=542
_globals['_AUTHMETADATA']._serialized_start=544
_globals['_AUTHMETADATA']._serialized_end=577
_globals['_EMPTY']._serialized_start=579
_globals['_EMPTY']._serialized_end=586
_globals['_WEATHERANALYTICSSERVICE']._serialized_start=589
_globals['_WEATHERANALYTICSSERVICE']._serialized_end=920
# @@protoc_insertion_point(module_scope)
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
import warnings
import weather_pb2 as weather__pb2
GRPC_GENERATED_VERSION = '1.76.0'
GRPC_VERSION = grpc.__version__
_version_not_supported = False
try:
from grpc._utilities import first_version_is_lower
_version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
except ImportError:
_version_not_supported = True
if _version_not_supported:
raise RuntimeError(
f'The grpc package installed is at version {GRPC_VERSION},'
+ ' but the generated code in weather_pb2_grpc.py depends on'
+ f' grpcio>={GRPC_GENERATED_VERSION}.'
+ f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}'
+ f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.'
)
class WeatherAnalyticsServiceStub(object):
"""Services and RPCs
----------------------------------------------------------------
1. Weather Sensor Service (Go)
*This service won't act as a server; it's the client for the main RPC.*
2. Weather Analytics Service (Python) - The central server
"""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.StreamWeatherData = channel.stream_unary(
'/weather_system.WeatherAnalyticsService/StreamWeatherData',
request_serializer=weather__pb2.WeatherData.SerializeToString,
response_deserializer=weather__pb2.WeatherStats.FromString,
_registered_method=True)
self.StreamAnalytics = channel.unary_stream(
'/weather_system.WeatherAnalyticsService/StreamAnalytics',
request_serializer=weather__pb2.Empty.SerializeToString,
response_deserializer=weather__pb2.WeatherStats.FromString,
_registered_method=True)
self.GetLastReport = channel.unary_unary(
'/weather_system.WeatherAnalyticsService/GetLastReport',
request_serializer=weather__pb2.Empty.SerializeToString,
response_deserializer=weather__pb2.WeatherStats.FromString,
_registered_method=True)
self.ControlSensor = channel.stream_stream(
'/weather_system.WeatherAnalyticsService/ControlSensor',
request_serializer=weather__pb2.AlertMessage.SerializeToString,
response_deserializer=weather__pb2.WeatherData.FromString,
_registered_method=True)
class WeatherAnalyticsServiceServicer(object):
"""Services and RPCs
----------------------------------------------------------------
1. Weather Sensor Service (Go)
*This service won't act as a server; it's the client for the main RPC.*
2. Weather Analytics Service (Python) - The central server
"""
def StreamWeatherData(self, request_iterator, context):
"""A. Client Streaming (Required: Receive from Sensor)
Sensor streams data; the server (Analytics) returns the final stats.
Auth note: Token validation will use gRPC Metadata, not this message field.
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def StreamAnalytics(self, request, context):
"""B. Server Streaming (Required: Send to Dashboard)
Dashboard requests (with Empty) a continuous stream of reports.
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def GetLastReport(self, request, context):
"""C. Unary RPC (Optional Requirement: Retrieve last report)
Dashboard manually requests the last stored stats.
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def ControlSensor(self, request_iterator, context):
"""D. Bidirectional Streaming (Academic addition)
Allows the server to send immediate alerts and potentially control the sensor.
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_WeatherAnalyticsServiceServicer_to_server(servicer, server):
rpc_method_handlers = {
'StreamWeatherData': grpc.stream_unary_rpc_method_handler(
servicer.StreamWeatherData,
request_deserializer=weather__pb2.WeatherData.FromString,
response_serializer=weather__pb2.WeatherStats.SerializeToString,
),
'StreamAnalytics': grpc.unary_stream_rpc_method_handler(
servicer.StreamAnalytics,
request_deserializer=weather__pb2.Empty.FromString,
response_serializer=weather__pb2.WeatherStats.SerializeToString,
),
'GetLastReport': grpc.unary_unary_rpc_method_handler(
servicer.GetLastReport,
request_deserializer=weather__pb2.Empty.FromString,
response_serializer=weather__pb2.WeatherStats.SerializeToString,
),
'ControlSensor': grpc.stream_stream_rpc_method_handler(
servicer.ControlSensor,
request_deserializer=weather__pb2.AlertMessage.FromString,
response_serializer=weather__pb2.WeatherData.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'weather_system.WeatherAnalyticsService', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
server.add_registered_method_handlers('weather_system.WeatherAnalyticsService', rpc_method_handlers)
# This class is part of an EXPERIMENTAL API.
class WeatherAnalyticsService(object):
"""Services and RPCs
----------------------------------------------------------------
1. Weather Sensor Service (Go)
*This service won't act as a server; it's the client for the main RPC.*
2. Weather Analytics Service (Python) - The central server
"""
@staticmethod
def StreamWeatherData(request_iterator,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.stream_unary(
request_iterator,
target,
'/weather_system.WeatherAnalyticsService/StreamWeatherData',
weather__pb2.WeatherData.SerializeToString,
weather__pb2.WeatherStats.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def StreamAnalytics(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_stream(
request,
target,
'/weather_system.WeatherAnalyticsService/StreamAnalytics',
weather__pb2.Empty.SerializeToString,
weather__pb2.WeatherStats.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def GetLastReport(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/weather_system.WeatherAnalyticsService/GetLastReport',
weather__pb2.Empty.SerializeToString,
weather__pb2.WeatherStats.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
@staticmethod
def ControlSensor(request_iterator,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.stream_stream(
request_iterator,
target,
'/weather_system.WeatherAnalyticsService/ControlSensor',
weather__pb2.AlertMessage.SerializeToString,
weather__pb2.WeatherData.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>javaServer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>javaServer</name>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- protobuf-java (لازم تكون موجودة لوحدها) -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>4.28.2</version>
</dependency>
<!-- عشان @Generated ما يطلعش أحمر -->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
<!-- DevTools (يعمل Reload تلقائي لما تغير الكود) -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.25.3:exe:windows-x86_64</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.68.0:exe:windows-x86_64</pluginArtifact>
<protoSourceRoot>${project.basedir}/../proto</protoSourceRoot>
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<clearOutputDirectory>false</clearOutputDirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package com.weather.monitoring.grpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Status;
import io.grpc.stub.StreamObserver;
// Generated files are now easily imported
import com.weather.monitoring.grpc.Weather;
import com.weather.monitoring.grpc.WeatherAnalyticsServiceGrpc;
import java.util.concurrent.TimeUnit;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DashboardClient {
private final ManagedChannel channel;
private final WeatherAnalyticsServiceGrpc.WeatherAnalyticsServiceBlockingStub blockingStub;
private final WeatherAnalyticsServiceGrpc.WeatherAnalyticsServiceStub asyncStub;
private static final String ANALYTICS_ADDRESS = "localhost:50051";
public DashboardClient(String host, int port) {
// Create an Insecure Channel
channel = ManagedChannelBuilder.forAddress(host, port)
.usePlaintext()
.build();
// Create stubs for synchronous (blocking) and asynchronous (streaming) calls
blockingStub = WeatherAnalyticsServiceGrpc.newBlockingStub(channel);
asyncStub = WeatherAnalyticsServiceGrpc.newStub(channel);
}
public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
/**
* Unary RPC: Manually requests the last stored report.
*/
public void getManualReport() {
System.out.println("\n--- 🔵 Requesting Manual Report (Unary RPC) ---");
try {
// Use Blocking Stub for Unary RPC
Weather.Empty request = Weather.Empty.newBuilder().build();
// Call GetLastReport
Weather.WeatherStats stats = blockingStub.getLastReport(request);
String reportTime = new SimpleDateFormat("HH:mm:ss").format(new Date(stats.getReportTime() * 1000L));
System.out.println("✅ Last Stored Report (at " + reportTime + "):");
System.out.printf(" > Avg Temp: %.2f C\n", stats.getAvgTemperature());
System.out.printf(" > Avg Humidity: %.2f %%\n", stats.getAvgHumidity());
System.out.printf(" > Avg Pressure: %.2f hPa\n", stats.getAvgPressure());
} catch (Exception e) {
Status status = Status.fromThrowable(e);
if (status.getCode() == Status.Code.NOT_FOUND) {
System.err.println("❌ Error: No reports currently stored in DB. Status: " + status.getDescription());
} else {
System.err.println("❌ Error requesting manual report. Details: " + status.getDescription());
}
}
}
/**
* Server Streaming: Subscribes to continuous reports from the Analytics Server.
*/
public void streamAnalytics() {
System.out.println("\n--- 🟢 Subscribing to Continuous Analysis Stream (Server Streaming) ---");
// Use Async Stub for Server Streaming
Weather.Empty request = Weather.Empty.newBuilder().build();
asyncStub.streamAnalytics(request, new StreamObserver<Weather.WeatherStats>() {
@Override
public void onNext(Weather.WeatherStats stats) {
// Receive data
String reportTime = new SimpleDateFormat("HH:mm:ss").format(new Date(stats.getReportTime() * 1000L));
System.out.printf("\n[%s] Updated Analysis Report -----------------\n", reportTime);
System.out.printf(" 🌡️ Avg Temp: %.2f C\n", stats.getAvgTemperature());
System.out.printf(" 💧 Avg Humidity: %.2f %%\n", stats.getAvgHumidity());
System.out.printf(" 💨 Avg Pressure: %.2f hPa\n", stats.getAvgPressure());
}
@Override
public void onError(Throwable t) {
// Handle errors
Status status = Status.fromThrowable(t);
System.err.println("❌ Stream Error from server. Details: " + status.getDescription());
}
@Override
public void onCompleted() {
// Stream completion (if server terminates it)
System.out.println("--- 🛑 Analysis Stream Completed ---");
}
});
}
public static void main(String[] args) throws InterruptedException {
DashboardClient client = new DashboardClient("localhost", 50051);
try {
// 1. Request a manual report immediately
client.getManualReport();
// 2. Start the continuous report stream (will run in the background)
client.streamAnalytics();
// Keep the main thread running indefinitely to receive the stream
Thread.sleep(Long.MAX_VALUE);
} finally {
client.shutdown();
}
}
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
package org.example;
//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
public static void main(String[] args) {
//TIP Press <shortcut actionId="ShowIntentionActions"/> with your caret at the highlighted text
// to see how IntelliJ IDEA suggests fixing it.
System.out.printf("Hello and welcome!");
for (int i = 1; i <= 5; i++) {
//TIP Press <shortcut actionId="Debug"/> to start debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.
System.out.println("i = " + i);
}
}
}
\ No newline at end of file
This diff is collapsed.
syntax = "proto3";
package weather_system;
option go_package = "sensor_go/weather_system";
option java_package = "com.weather.monitoring.grpc";
option java_outer_classname = "Weather";
// Messages
// ----------------------------------------------------------------
// 1. Weather Data sent from the sensor (WeatherData)
message WeatherData {
double temperature_celsius = 1; // Temperature in Celsius (e.g., 25.5)
double humidity_percent = 2; // Humidity percentage (e.g., 60.0)
double pressure_hpa = 3; // Pressure in Hectopascals (e.g., 1012.3)
int64 timestamp = 4; // Unix timestamp of the reading
string station_id = 5; // Identifier of the weather station
}
// 2. Calculated Weather Statistics (WeatherStats)
message WeatherStats {
double avg_temperature = 1;
double avg_humidity = 2;
double avg_pressure = 3;
int64 report_time = 4;
// Optional field to send alerts within the data stream
AlertMessage current_alert = 5;
}
// 3. Alert Message (AlertMessage)
message AlertMessage {
enum AlertType {
UNKNOWN = 0;
HIGH_TEMPERATURE = 1;
LOW_PRESSURE = 2;
HIGH_HUMIDITY = 3;
NORMAL = 4;
}
AlertType type = 1;
string message = 2;
double value = 3;
int64 timestamp = 4;
}
// 4. Authentication Metadata (AuthMetadata)
// *Note: Token is typically sent via gRPC Metadata, but defined here for completeness*
message AuthMetadata {
string api_token = 1;
}
// Empty message
message Empty {}
// Services and RPCs
// ----------------------------------------------------------------
// 1. Weather Sensor Service (Go)
// *This service won't act as a server; it's the client for the main RPC.*
// 2. Weather Analytics Service (Python) - The central server
service WeatherAnalyticsService {
// A. Client Streaming (Required: Receive from Sensor)
// Sensor streams data; the server (Analytics) returns the final stats.
// Auth note: Token validation will use gRPC Metadata, not this message field.
rpc StreamWeatherData(stream WeatherData) returns (WeatherStats);
// B. Server Streaming (Required: Send to Dashboard)
// Dashboard requests (with Empty) a continuous stream of reports.
rpc StreamAnalytics(Empty) returns (stream WeatherStats);
// C. Unary RPC (Optional Requirement: Retrieve last report)
// Dashboard manually requests the last stored stats.
rpc GetLastReport(Empty) returns (WeatherStats);
// D. Bidirectional Streaming (Academic addition)
// Allows the server to send immediate alerts and potentially control the sensor.
rpc ControlSensor(stream AlertMessage) returns (stream WeatherData);
}
\ No newline at end of file
C:\Users\J.N\IdeaProjects\weather_mulltilanguage_grpc\dashboard_java\src\main\java\com\weather\monitoring\grpc\DashboardClient.java
C:\Users\J.N\IdeaProjects\weather_mulltilanguage_grpc\dashboard_java\src\main\java\com\weather\monitoring\grpc\Weather.java
C:\Users\J.N\IdeaProjects\weather_mulltilanguage_grpc\dashboard_java\src\main\java\com\weather\monitoring\grpc\WeatherAnalyticsServiceGrpc.java
C:\Users\J.N\IdeaProjects\weather_mulltilanguage_grpc\dashboard_java\src\main\java\org\example\Main.java
C:\Users\J.N\IdeaProjects\weather_mulltilanguage_grpc\dashboard_java\src\main\java\weather_system\Weather.java
C:\Users\J.N\IdeaProjects\weather_mulltilanguage_grpc\dashboard_java\src\main\java\weather_system\WeatherAnalyticsServiceGrpc.java
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option go_package = "google.golang.org/protobuf/types/known/anypb";
option java_package = "com.google.protobuf";
option java_outer_classname = "AnyProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
// `Any` contains an arbitrary serialized protocol buffer message along with a
// URL that describes the type of the serialized message.
//
// Protobuf library provides support to pack/unpack Any values in the form
// of utility functions or additional generated methods of the Any type.
//
// Example 1: Pack and unpack a message in C++.
//
// Foo foo = ...;
// Any any;
// any.PackFrom(foo);
// ...
// if (any.UnpackTo(&foo)) {
// ...
// }
//
// Example 2: Pack and unpack a message in Java.
//
// Foo foo = ...;
// Any any = Any.pack(foo);
// ...
// if (any.is(Foo.class)) {
// foo = any.unpack(Foo.class);
// }
// // or ...
// if (any.isSameTypeAs(Foo.getDefaultInstance())) {
// foo = any.unpack(Foo.getDefaultInstance());
// }
//
// Example 3: Pack and unpack a message in Python.
//
// foo = Foo(...)
// any = Any()
// any.Pack(foo)
// ...
// if any.Is(Foo.DESCRIPTOR):
// any.Unpack(foo)
// ...
//
// Example 4: Pack and unpack a message in Go
//
// foo := &pb.Foo{...}
// any, err := anypb.New(foo)
// if err != nil {
// ...
// }
// ...
// foo := &pb.Foo{}
// if err := any.UnmarshalTo(foo); err != nil {
// ...
// }
//
// The pack methods provided by protobuf library will by default use
// 'type.googleapis.com/full.type.name' as the type URL and the unpack
// methods only use the fully qualified type name after the last '/'
// in the type URL, for example "foo.bar.com/x/y.z" will yield type
// name "y.z".
//
// JSON
// ====
// The JSON representation of an `Any` value uses the regular
// representation of the deserialized, embedded message, with an
// additional field `@type` which contains the type URL. Example:
//
// package google.profile;
// message Person {
// string first_name = 1;
// string last_name = 2;
// }
//
// {
// "@type": "type.googleapis.com/google.profile.Person",
// "firstName": <string>,
// "lastName": <string>
// }
//
// If the embedded message type is well-known and has a custom JSON
// representation, that representation will be embedded adding a field
// `value` which holds the custom JSON in addition to the `@type`
// field. Example (for message [google.protobuf.Duration][]):
//
// {
// "@type": "type.googleapis.com/google.protobuf.Duration",
// "value": "1.212s"
// }
//
message Any {
// A URL/resource name that uniquely identifies the type of the serialized
// protocol buffer message. This string must contain at least
// one "/" character. The last segment of the URL's path must represent
// the fully qualified name of the type (as in
// `path/google.protobuf.Duration`). The name should be in a canonical form
// (e.g., leading "." is not accepted).
//
// In practice, teams usually precompile into the binary all types that they
// expect it to use in the context of Any. However, for URLs which use the
// scheme `http`, `https`, or no scheme, one can optionally set up a type
// server that maps type URLs to message definitions as follows:
//
// * If no scheme is provided, `https` is assumed.
// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
// value in binary format, or produce an error.
// * Applications are allowed to cache lookup results based on the
// URL, or have them precompiled into a binary to avoid any
// lookup. Therefore, binary compatibility needs to be preserved
// on changes to types. (Use versioned type names to manage
// breaking changes.)
//
// Note: this functionality is not currently available in the official
// protobuf release, and it is not used for type URLs beginning with
// type.googleapis.com. As of May 2023, there are no widely used type server
// implementations and no plans to implement one.
//
// Schemes other than `http`, `https` (or the empty scheme) might be
// used with implementation specific semantics.
//
string type_url = 1;
// Must be a valid serialized protocol buffer of the above specified type.
bytes value = 2;
}
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
import "google/protobuf/source_context.proto";
import "google/protobuf/type.proto";
option java_package = "com.google.protobuf";
option java_outer_classname = "ApiProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option go_package = "google.golang.org/protobuf/types/known/apipb";
// Api is a light-weight descriptor for an API Interface.
//
// Interfaces are also described as "protocol buffer services" in some contexts,
// such as by the "service" keyword in a .proto file, but they are different
// from API Services, which represent a concrete implementation of an interface
// as opposed to simply a description of methods and bindings. They are also
// sometimes simply referred to as "APIs" in other contexts, such as the name of
// this message itself. See https://cloud.google.com/apis/design/glossary for
// detailed terminology.
message Api {
// The fully qualified name of this interface, including package name
// followed by the interface's simple name.
string name = 1;
// The methods of this interface, in unspecified order.
repeated Method methods = 2;
// Any metadata attached to the interface.
repeated Option options = 3;
// A version string for this interface. If specified, must have the form
// `major-version.minor-version`, as in `1.10`. If the minor version is
// omitted, it defaults to zero. If the entire version field is empty, the
// major version is derived from the package name, as outlined below. If the
// field is not empty, the version in the package name will be verified to be
// consistent with what is provided here.
//
// The versioning schema uses [semantic
// versioning](http://semver.org) where the major version number
// indicates a breaking change and the minor version an additive,
// non-breaking change. Both version numbers are signals to users
// what to expect from different versions, and should be carefully
// chosen based on the product plan.
//
// The major version is also reflected in the package name of the
// interface, which must end in `v<major-version>`, as in
// `google.feature.v1`. For major versions 0 and 1, the suffix can
// be omitted. Zero major versions must only be used for
// experimental, non-GA interfaces.
//
string version = 4;
// Source context for the protocol buffer service represented by this
// message.
SourceContext source_context = 5;
// Included interfaces. See [Mixin][].
repeated Mixin mixins = 6;
// The source syntax of the service.
Syntax syntax = 7;
}
// Method represents a method of an API interface.
message Method {
// The simple name of this method.
string name = 1;
// A URL of the input message type.
string request_type_url = 2;
// If true, the request is streamed.
bool request_streaming = 3;
// The URL of the output message type.
string response_type_url = 4;
// If true, the response is streamed.
bool response_streaming = 5;
// Any metadata attached to the method.
repeated Option options = 6;
// The source syntax of this method.
Syntax syntax = 7;
}
// Declares an API Interface to be included in this interface. The including
// interface must redeclare all the methods from the included interface, but
// documentation and options are inherited as follows:
//
// - If after comment and whitespace stripping, the documentation
// string of the redeclared method is empty, it will be inherited
// from the original method.
//
// - Each annotation belonging to the service config (http,
// visibility) which is not set in the redeclared method will be
// inherited.
//
// - If an http annotation is inherited, the path pattern will be
// modified as follows. Any version prefix will be replaced by the
// version of the including interface plus the [root][] path if
// specified.
//
// Example of a simple mixin:
//
// package google.acl.v1;
// service AccessControl {
// // Get the underlying ACL object.
// rpc GetAcl(GetAclRequest) returns (Acl) {
// option (google.api.http).get = "/v1/{resource=**}:getAcl";
// }
// }
//
// package google.storage.v2;
// service Storage {
// rpc GetAcl(GetAclRequest) returns (Acl);
//
// // Get a data record.
// rpc GetData(GetDataRequest) returns (Data) {
// option (google.api.http).get = "/v2/{resource=**}";
// }
// }
//
// Example of a mixin configuration:
//
// apis:
// - name: google.storage.v2.Storage
// mixins:
// - name: google.acl.v1.AccessControl
//
// The mixin construct implies that all methods in `AccessControl` are
// also declared with same name and request/response types in
// `Storage`. A documentation generator or annotation processor will
// see the effective `Storage.GetAcl` method after inherting
// documentation and annotations as follows:
//
// service Storage {
// // Get the underlying ACL object.
// rpc GetAcl(GetAclRequest) returns (Acl) {
// option (google.api.http).get = "/v2/{resource=**}:getAcl";
// }
// ...
// }
//
// Note how the version in the path pattern changed from `v1` to `v2`.
//
// If the `root` field in the mixin is specified, it should be a
// relative path under which inherited HTTP paths are placed. Example:
//
// apis:
// - name: google.storage.v2.Storage
// mixins:
// - name: google.acl.v1.AccessControl
// root: acls
//
// This implies the following inherited HTTP annotation:
//
// service Storage {
// // Get the underlying ACL object.
// rpc GetAcl(GetAclRequest) returns (Acl) {
// option (google.api.http).get = "/v2/acls/{resource=**}:getAcl";
// }
// ...
// }
message Mixin {
// The fully qualified name of the interface which is included.
string name = 1;
// If non-empty specifies a path under which inherited HTTP paths
// are rooted.
string root = 2;
}
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option cc_enable_arenas = true;
option go_package = "google.golang.org/protobuf/types/known/durationpb";
option java_package = "com.google.protobuf";
option java_outer_classname = "DurationProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
// A Duration represents a signed, fixed-length span of time represented
// as a count of seconds and fractions of seconds at nanosecond
// resolution. It is independent of any calendar and concepts like "day"
// or "month". It is related to Timestamp in that the difference between
// two Timestamp values is a Duration and it can be added or subtracted
// from a Timestamp. Range is approximately +-10,000 years.
//
// # Examples
//
// Example 1: Compute Duration from two Timestamps in pseudo code.
//
// Timestamp start = ...;
// Timestamp end = ...;
// Duration duration = ...;
//
// duration.seconds = end.seconds - start.seconds;
// duration.nanos = end.nanos - start.nanos;
//
// if (duration.seconds < 0 && duration.nanos > 0) {
// duration.seconds += 1;
// duration.nanos -= 1000000000;
// } else if (duration.seconds > 0 && duration.nanos < 0) {
// duration.seconds -= 1;
// duration.nanos += 1000000000;
// }
//
// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code.
//
// Timestamp start = ...;
// Duration duration = ...;
// Timestamp end = ...;
//
// end.seconds = start.seconds + duration.seconds;
// end.nanos = start.nanos + duration.nanos;
//
// if (end.nanos < 0) {
// end.seconds -= 1;
// end.nanos += 1000000000;
// } else if (end.nanos >= 1000000000) {
// end.seconds += 1;
// end.nanos -= 1000000000;
// }
//
// Example 3: Compute Duration from datetime.timedelta in Python.
//
// td = datetime.timedelta(days=3, minutes=10)
// duration = Duration()
// duration.FromTimedelta(td)
//
// # JSON Mapping
//
// In JSON format, the Duration type is encoded as a string rather than an
// object, where the string ends in the suffix "s" (indicating seconds) and
// is preceded by the number of seconds, with nanoseconds expressed as
// fractional seconds. For example, 3 seconds with 0 nanoseconds should be
// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should
// be expressed in JSON format as "3.000000001s", and 3 seconds and 1
// microsecond should be expressed in JSON format as "3.000001s".
//
message Duration {
// Signed seconds of the span of time. Must be from -315,576,000,000
// to +315,576,000,000 inclusive. Note: these bounds are computed from:
// 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
int64 seconds = 1;
// Signed fractions of a second at nanosecond resolution of the span
// of time. Durations less than one second are represented with a 0
// `seconds` field and a positive or negative `nanos` field. For durations
// of one second or more, a non-zero value for the `nanos` field must be
// of the same sign as the `seconds` field. Must be from -999,999,999
// to +999,999,999 inclusive.
int32 nanos = 2;
}
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option go_package = "google.golang.org/protobuf/types/known/emptypb";
option java_package = "com.google.protobuf";
option java_outer_classname = "EmptyProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option cc_enable_arenas = true;
// A generic empty message that you can re-use to avoid defining duplicated
// empty messages in your APIs. A typical example is to use it as the request
// or the response type of an API method. For instance:
//
// service Foo {
// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
// }
//
message Empty {}
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option java_package = "com.google.protobuf";
option java_outer_classname = "FieldMaskProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option go_package = "google.golang.org/protobuf/types/known/fieldmaskpb";
option cc_enable_arenas = true;
// `FieldMask` represents a set of symbolic field paths, for example:
//
// paths: "f.a"
// paths: "f.b.d"
//
// Here `f` represents a field in some root message, `a` and `b`
// fields in the message found in `f`, and `d` a field found in the
// message in `f.b`.
//
// Field masks are used to specify a subset of fields that should be
// returned by a get operation or modified by an update operation.
// Field masks also have a custom JSON encoding (see below).
//
// # Field Masks in Projections
//
// When used in the context of a projection, a response message or
// sub-message is filtered by the API to only contain those fields as
// specified in the mask. For example, if the mask in the previous
// example is applied to a response message as follows:
//
// f {
// a : 22
// b {
// d : 1
// x : 2
// }
// y : 13
// }
// z: 8
//
// The result will not contain specific values for fields x,y and z
// (their value will be set to the default, and omitted in proto text
// output):
//
//
// f {
// a : 22
// b {
// d : 1
// }
// }
//
// A repeated field is not allowed except at the last position of a
// paths string.
//
// If a FieldMask object is not present in a get operation, the
// operation applies to all fields (as if a FieldMask of all fields
// had been specified).
//
// Note that a field mask does not necessarily apply to the
// top-level response message. In case of a REST get operation, the
// field mask applies directly to the response, but in case of a REST
// list operation, the mask instead applies to each individual message
// in the returned resource list. In case of a REST custom method,
// other definitions may be used. Where the mask applies will be
// clearly documented together with its declaration in the API. In
// any case, the effect on the returned resource/resources is required
// behavior for APIs.
//
// # Field Masks in Update Operations
//
// A field mask in update operations specifies which fields of the
// targeted resource are going to be updated. The API is required
// to only change the values of the fields as specified in the mask
// and leave the others untouched. If a resource is passed in to
// describe the updated values, the API ignores the values of all
// fields not covered by the mask.
//
// If a repeated field is specified for an update operation, new values will
// be appended to the existing repeated field in the target resource. Note that
// a repeated field is only allowed in the last position of a `paths` string.
//
// If a sub-message is specified in the last position of the field mask for an
// update operation, then new value will be merged into the existing sub-message
// in the target resource.
//
// For example, given the target message:
//
// f {
// b {
// d: 1
// x: 2
// }
// c: [1]
// }
//
// And an update message:
//
// f {
// b {
// d: 10
// }
// c: [2]
// }
//
// then if the field mask is:
//
// paths: ["f.b", "f.c"]
//
// then the result will be:
//
// f {
// b {
// d: 10
// x: 2
// }
// c: [1, 2]
// }
//
// An implementation may provide options to override this default behavior for
// repeated and message fields.
//
// In order to reset a field's value to the default, the field must
// be in the mask and set to the default value in the provided resource.
// Hence, in order to reset all fields of a resource, provide a default
// instance of the resource and set all fields in the mask, or do
// not provide a mask as described below.
//
// If a field mask is not present on update, the operation applies to
// all fields (as if a field mask of all fields has been specified).
// Note that in the presence of schema evolution, this may mean that
// fields the client does not know and has therefore not filled into
// the request will be reset to their default. If this is unwanted
// behavior, a specific service may require a client to always specify
// a field mask, producing an error if not.
//
// As with get operations, the location of the resource which
// describes the updated values in the request message depends on the
// operation kind. In any case, the effect of the field mask is
// required to be honored by the API.
//
// ## Considerations for HTTP REST
//
// The HTTP kind of an update operation which uses a field mask must
// be set to PATCH instead of PUT in order to satisfy HTTP semantics
// (PUT must only be used for full updates).
//
// # JSON Encoding of Field Masks
//
// In JSON, a field mask is encoded as a single string where paths are
// separated by a comma. Fields name in each path are converted
// to/from lower-camel naming conventions.
//
// As an example, consider the following message declarations:
//
// message Profile {
// User user = 1;
// Photo photo = 2;
// }
// message User {
// string display_name = 1;
// string address = 2;
// }
//
// In proto a field mask for `Profile` may look as such:
//
// mask {
// paths: "user.display_name"
// paths: "photo"
// }
//
// In JSON, the same mask is represented as below:
//
// {
// mask: "user.displayName,photo"
// }
//
// # Field Masks and Oneof Fields
//
// Field masks treat fields in oneofs just as regular fields. Consider the
// following message:
//
// message SampleMessage {
// oneof test_oneof {
// string name = 4;
// SubMessage sub_message = 9;
// }
// }
//
// The field mask can be:
//
// mask {
// paths: "name"
// }
//
// Or:
//
// mask {
// paths: "sub_message"
// }
//
// Note that oneof type names ("test_oneof" in this case) cannot be used in
// paths.
//
// ## Field Mask Verification
//
// The implementation of any API method which has a FieldMask type field in the
// request should verify the included field paths, and return an
// `INVALID_ARGUMENT` error if any path is unmappable.
message FieldMask {
// The set of field mask paths.
repeated string paths = 1;
}
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
syntax = "proto2";
package pb;
import "google/protobuf/descriptor.proto";
option java_package = "com.google.protobuf";
option java_outer_classname = "JavaFeaturesProto";
extend google.protobuf.FeatureSet {
optional JavaFeatures java = 1001;
}
message JavaFeatures {
// Whether or not to treat an enum field as closed. This option is only
// applicable to enum fields, and will be removed in the future. It is
// consistent with the legacy behavior of using proto3 enum types for proto2
// fields.
optional bool legacy_closed_enum = 1 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_FIELD,
targets = TARGET_TYPE_FILE,
feature_support = {
edition_introduced: EDITION_2023,
edition_deprecated: EDITION_2023,
deprecation_warning: "The legacy closed enum treatment in Java is "
"deprecated and is scheduled to be removed in "
"edition 2025. Mark enum type on the enum "
"definitions themselves rather than on fields.",
},
edition_defaults = { edition: EDITION_LEGACY, value: "true" },
edition_defaults = { edition: EDITION_PROTO3, value: "false" }
];
// The UTF8 validation strategy to use. See go/editions-utf8-validation for
// more information on this feature.
enum Utf8Validation {
// Invalid default, which should never be used.
UTF8_VALIDATION_UNKNOWN = 0;
// Respect the UTF8 validation behavior specified by the global
// utf8_validation feature.
DEFAULT = 1;
// Verifies UTF8 validity overriding the global utf8_validation
// feature. This represents the legacy java_string_check_utf8 option.
VERIFY = 2;
}
optional Utf8Validation utf8_validation = 2 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_FIELD,
targets = TARGET_TYPE_FILE,
feature_support = {
edition_introduced: EDITION_2023,
edition_deprecated: EDITION_2024,
deprecation_warning: "The Java-specific utf8 validation feature is "
"deprecated and is scheduled to be removed in "
"edition 2025. Utf8 validation behavior should "
"use the global cross-language utf8_validation "
"feature.",
},
edition_defaults = { edition: EDITION_LEGACY, value: "DEFAULT" }
];
}
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option java_package = "com.google.protobuf";
option java_outer_classname = "SourceContextProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option go_package = "google.golang.org/protobuf/types/known/sourcecontextpb";
// `SourceContext` represents information about the source of a
// protobuf element, like the file in which it is defined.
message SourceContext {
// The path-qualified name of the .proto file that contained the associated
// protobuf element. For example: `"google/protobuf/source_context.proto"`.
string file_name = 1;
}
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option cc_enable_arenas = true;
option go_package = "google.golang.org/protobuf/types/known/structpb";
option java_package = "com.google.protobuf";
option java_outer_classname = "StructProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
// `Struct` represents a structured data value, consisting of fields
// which map to dynamically typed values. In some languages, `Struct`
// might be supported by a native representation. For example, in
// scripting languages like JS a struct is represented as an
// object. The details of that representation are described together
// with the proto support for the language.
//
// The JSON representation for `Struct` is JSON object.
message Struct {
// Unordered map of dynamically typed values.
map<string, Value> fields = 1;
}
// `Value` represents a dynamically typed value which can be either
// null, a number, a string, a boolean, a recursive struct value, or a
// list of values. A producer of value is expected to set one of these
// variants. Absence of any variant indicates an error.
//
// The JSON representation for `Value` is JSON value.
message Value {
// The kind of value.
oneof kind {
// Represents a null value.
NullValue null_value = 1;
// Represents a double value.
double number_value = 2;
// Represents a string value.
string string_value = 3;
// Represents a boolean value.
bool bool_value = 4;
// Represents a structured value.
Struct struct_value = 5;
// Represents a repeated `Value`.
ListValue list_value = 6;
}
}
// `NullValue` is a singleton enumeration to represent the null value for the
// `Value` type union.
//
// The JSON representation for `NullValue` is JSON `null`.
enum NullValue {
// Null value.
NULL_VALUE = 0;
}
// `ListValue` is a wrapper around a repeated field of values.
//
// The JSON representation for `ListValue` is JSON array.
message ListValue {
// Repeated field of dynamically typed values.
repeated Value values = 1;
}
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option cc_enable_arenas = true;
option go_package = "google.golang.org/protobuf/types/known/timestamppb";
option java_package = "com.google.protobuf";
option java_outer_classname = "TimestampProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
// A Timestamp represents a point in time independent of any time zone or local
// calendar, encoded as a count of seconds and fractions of seconds at
// nanosecond resolution. The count is relative to an epoch at UTC midnight on
// January 1, 1970, in the proleptic Gregorian calendar which extends the
// Gregorian calendar backwards to year one.
//
// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
// second table is needed for interpretation, using a [24-hour linear
// smear](https://developers.google.com/time/smear).
//
// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
// restricting to that range, we ensure that we can convert to and from [RFC
// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
//
// # Examples
//
// Example 1: Compute Timestamp from POSIX `time()`.
//
// Timestamp timestamp;
// timestamp.set_seconds(time(NULL));
// timestamp.set_nanos(0);
//
// Example 2: Compute Timestamp from POSIX `gettimeofday()`.
//
// struct timeval tv;
// gettimeofday(&tv, NULL);
//
// Timestamp timestamp;
// timestamp.set_seconds(tv.tv_sec);
// timestamp.set_nanos(tv.tv_usec * 1000);
//
// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
//
// FILETIME ft;
// GetSystemTimeAsFileTime(&ft);
// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
//
// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
// Timestamp timestamp;
// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
//
// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
//
// long millis = System.currentTimeMillis();
//
// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
// .setNanos((int) ((millis % 1000) * 1000000)).build();
//
// Example 5: Compute Timestamp from Java `Instant.now()`.
//
// Instant now = Instant.now();
//
// Timestamp timestamp =
// Timestamp.newBuilder().setSeconds(now.getEpochSecond())
// .setNanos(now.getNano()).build();
//
// Example 6: Compute Timestamp from current time in Python.
//
// timestamp = Timestamp()
// timestamp.GetCurrentTime()
//
// # JSON Mapping
//
// In JSON format, the Timestamp type is encoded as a string in the
// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
// where {year} is always expressed using four digits while {month}, {day},
// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
// is required. A proto3 JSON serializer should always use UTC (as indicated by
// "Z") when printing the Timestamp type and a proto3 JSON parser should be
// able to accept both UTC and other timezones (as indicated by an offset).
//
// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
// 01:30 UTC on January 15, 2017.
//
// In JavaScript, one can convert a Date object to this format using the
// standard
// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
// method. In Python, a standard `datetime.datetime` object can be converted
// to this format using
// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
// the Joda Time's [`ISODateTimeFormat.dateTime()`](
// http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()
// ) to obtain a formatter capable of generating timestamps in this format.
//
message Timestamp {
// Represents seconds of UTC time since Unix epoch
// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
// 9999-12-31T23:59:59Z inclusive.
int64 seconds = 1;
// Non-negative fractions of a second at nanosecond resolution. Negative
// second values with fractions must still have non-negative nanos values
// that count forward in time. Must be from 0 to 999,999,999
// inclusive.
int32 nanos = 2;
}
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
import "google/protobuf/any.proto";
import "google/protobuf/source_context.proto";
option cc_enable_arenas = true;
option java_package = "com.google.protobuf";
option java_outer_classname = "TypeProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option go_package = "google.golang.org/protobuf/types/known/typepb";
// A protocol buffer message type.
message Type {
// The fully qualified message name.
string name = 1;
// The list of fields.
repeated Field fields = 2;
// The list of types appearing in `oneof` definitions in this type.
repeated string oneofs = 3;
// The protocol buffer options.
repeated Option options = 4;
// The source context.
SourceContext source_context = 5;
// The source syntax.
Syntax syntax = 6;
// The source edition string, only valid when syntax is SYNTAX_EDITIONS.
string edition = 7;
}
// A single field of a message type.
message Field {
// Basic field types.
enum Kind {
// Field type unknown.
TYPE_UNKNOWN = 0;
// Field type double.
TYPE_DOUBLE = 1;
// Field type float.
TYPE_FLOAT = 2;
// Field type int64.
TYPE_INT64 = 3;
// Field type uint64.
TYPE_UINT64 = 4;
// Field type int32.
TYPE_INT32 = 5;
// Field type fixed64.
TYPE_FIXED64 = 6;
// Field type fixed32.
TYPE_FIXED32 = 7;
// Field type bool.
TYPE_BOOL = 8;
// Field type string.
TYPE_STRING = 9;
// Field type group. Proto2 syntax only, and deprecated.
TYPE_GROUP = 10;
// Field type message.
TYPE_MESSAGE = 11;
// Field type bytes.
TYPE_BYTES = 12;
// Field type uint32.
TYPE_UINT32 = 13;
// Field type enum.
TYPE_ENUM = 14;
// Field type sfixed32.
TYPE_SFIXED32 = 15;
// Field type sfixed64.
TYPE_SFIXED64 = 16;
// Field type sint32.
TYPE_SINT32 = 17;
// Field type sint64.
TYPE_SINT64 = 18;
}
// Whether a field is optional, required, or repeated.
enum Cardinality {
// For fields with unknown cardinality.
CARDINALITY_UNKNOWN = 0;
// For optional fields.
CARDINALITY_OPTIONAL = 1;
// For required fields. Proto2 syntax only.
CARDINALITY_REQUIRED = 2;
// For repeated fields.
CARDINALITY_REPEATED = 3;
}
// The field type.
Kind kind = 1;
// The field cardinality.
Cardinality cardinality = 2;
// The field number.
int32 number = 3;
// The field name.
string name = 4;
// The field type URL, without the scheme, for message or enumeration
// types. Example: `"type.googleapis.com/google.protobuf.Timestamp"`.
string type_url = 6;
// The index of the field type in `Type.oneofs`, for message or enumeration
// types. The first type has index 1; zero means the type is not in the list.
int32 oneof_index = 7;
// Whether to use alternative packed wire representation.
bool packed = 8;
// The protocol buffer options.
repeated Option options = 9;
// The field JSON name.
string json_name = 10;
// The string value of the default value of this field. Proto2 syntax only.
string default_value = 11;
}
// Enum type definition.
message Enum {
// Enum type name.
string name = 1;
// Enum value definitions.
repeated EnumValue enumvalue = 2;
// Protocol buffer options.
repeated Option options = 3;
// The source context.
SourceContext source_context = 4;
// The source syntax.
Syntax syntax = 5;
// The source edition string, only valid when syntax is SYNTAX_EDITIONS.
string edition = 6;
}
// Enum value definition.
message EnumValue {
// Enum value name.
string name = 1;
// Enum value number.
int32 number = 2;
// Protocol buffer options.
repeated Option options = 3;
}
// A protocol buffer option, which can be attached to a message, field,
// enumeration, etc.
message Option {
// The option's name. For protobuf built-in options (options defined in
// descriptor.proto), this is the short name. For example, `"map_entry"`.
// For custom options, it should be the fully-qualified name. For example,
// `"google.api.http"`.
string name = 1;
// The option's value packed in an Any message. If the value is a primitive,
// the corresponding wrapper type defined in google/protobuf/wrappers.proto
// should be used. If the value is an enum, it should be stored as an int32
// value using the google.protobuf.Int32Value type.
Any value = 2;
}
// The syntax in which a protocol buffer element is defined.
enum Syntax {
// Syntax `proto2`.
SYNTAX_PROTO2 = 0;
// Syntax `proto3`.
SYNTAX_PROTO3 = 1;
// Syntax `editions`.
SYNTAX_EDITIONS = 2;
}
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Wrappers for primitive (non-message) types. These types are useful
// for embedding primitives in the `google.protobuf.Any` type and for places
// where we need to distinguish between the absence of a primitive
// typed field and its default value.
//
// These wrappers have no meaningful use within repeated fields as they lack
// the ability to detect presence on individual elements.
// These wrappers have no meaningful use within a map or a oneof since
// individual entries of a map or fields of a oneof can already detect presence.
syntax = "proto3";
package google.protobuf;
option cc_enable_arenas = true;
option go_package = "google.golang.org/protobuf/types/known/wrapperspb";
option java_package = "com.google.protobuf";
option java_outer_classname = "WrappersProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
// Wrapper message for `double`.
//
// The JSON representation for `DoubleValue` is JSON number.
message DoubleValue {
// The double value.
double value = 1;
}
// Wrapper message for `float`.
//
// The JSON representation for `FloatValue` is JSON number.
message FloatValue {
// The float value.
float value = 1;
}
// Wrapper message for `int64`.
//
// The JSON representation for `Int64Value` is JSON string.
message Int64Value {
// The int64 value.
int64 value = 1;
}
// Wrapper message for `uint64`.
//
// The JSON representation for `UInt64Value` is JSON string.
message UInt64Value {
// The uint64 value.
uint64 value = 1;
}
// Wrapper message for `int32`.
//
// The JSON representation for `Int32Value` is JSON number.
message Int32Value {
// The int32 value.
int32 value = 1;
}
// Wrapper message for `uint32`.
//
// The JSON representation for `UInt32Value` is JSON number.
message UInt32Value {
// The uint32 value.
uint32 value = 1;
}
// Wrapper message for `bool`.
//
// The JSON representation for `BoolValue` is JSON `true` and `false`.
message BoolValue {
// The bool value.
bool value = 1;
}
// Wrapper message for `string`.
//
// The JSON representation for `StringValue` is JSON string.
message StringValue {
// The string value.
string value = 1;
}
// Wrapper message for `bytes`.
//
// The JSON representation for `BytesValue` is JSON string.
message BytesValue {
// The bytes value.
bytes value = 1;
}
syntax = "proto3";
package weather_system;
option go_package = "sensor_go/weather_system";
option java_package = "com.weather.monitoring.grpc";
option java_outer_classname = "Weather";
// Messages
// ----------------------------------------------------------------
// 1. Weather Data sent from the sensor (WeatherData)
message WeatherData {
double temperature_celsius = 1; // Temperature in Celsius (e.g., 25.5)
double humidity_percent = 2; // Humidity percentage (e.g., 60.0)
double pressure_hpa = 3; // Pressure in Hectopascals (e.g., 1012.3)
int64 timestamp = 4; // Unix timestamp of the reading
string station_id = 5; // Identifier of the weather station
}
// 2. Calculated Weather Statistics (WeatherStats)
message WeatherStats {
double avg_temperature = 1;
double avg_humidity = 2;
double avg_pressure = 3;
int64 report_time = 4;
// Optional field to send alerts within the data stream
AlertMessage current_alert = 5;
}
// 3. Alert Message (AlertMessage)
message AlertMessage {
enum AlertType {
UNKNOWN = 0;
HIGH_TEMPERATURE = 1;
LOW_PRESSURE = 2;
HIGH_HUMIDITY = 3;
NORMAL = 4;
}
AlertType type = 1;
string message = 2;
double value = 3;
int64 timestamp = 4;
}
// 4. Authentication Metadata (AuthMetadata)
// *Note: Token is typically sent via gRPC Metadata, but defined here for completeness*
message AuthMetadata {
string api_token = 1;
}
// Empty message
message Empty {}
// Services and RPCs
// ----------------------------------------------------------------
// 1. Weather Sensor Service (Go)
// *This service won't act as a server; it's the client for the main RPC.*
// 2. Weather Analytics Service (Python) - The central server
service WeatherAnalyticsService {
// A. Client Streaming (Required: Receive from Sensor)
// Sensor streams data; the server (Analytics) returns the final stats.
// Auth note: Token validation will use gRPC Metadata, not this message field.
rpc StreamWeatherData(stream WeatherData) returns (WeatherStats);
// B. Server Streaming (Required: Send to Dashboard)
// Dashboard requests (with Empty) a continuous stream of reports.
rpc StreamAnalytics(Empty) returns (stream WeatherStats);
// C. Unary RPC (Optional Requirement: Retrieve last report)
// Dashboard manually requests the last stored stats.
rpc GetLastReport(Empty) returns (WeatherStats);
// D. Bidirectional Streaming (Academic addition)
// Allows the server to send immediate alerts and potentially control the sensor.
rpc ControlSensor(stream AlertMessage) returns (stream WeatherData);
}
\ No newline at end of file
module sensor_go
go 1.25.4
require (
golang.org/x/net v0.42.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b // indirect
google.golang.org/grpc v1.76.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
)
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b h1:zPKJod4w6F1+nRGDI9ubnXYhU9NSWoFAijkHkUXeTK8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
package main
import (
"context"
// "fmt"
"log"
"math/rand"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
// This import path must match the module name and package name
pb "sensor_go/weather_system"
)
const (
address = "localhost:50051"
stationID = "STATION_GO_001"
// The correct secret key, must match the one in analytics_server.py
VALID_AUTH_KEY = "SECRET_WEATHER_KEY_2025"
)
// Simulates sending streaming data (Client Streaming)
func streamWeatherData(client pb.WeatherAnalyticsServiceClient, authKey string) {
// 1. Setup Metadata for Authentication:
// The key is "authorization" and the value must start with "Bearer ".
md := metadata.Pairs("authorization", "Bearer "+authKey)
ctx := metadata.NewOutgoingContext(context.Background(), md)
// Open the data stream (Client Streaming)
stream, err := client.StreamWeatherData(ctx)
if err != nil {
log.Printf("❌ Failed to open stream: %v", err)
return
}
log.Printf("--- Starting data transmission (Key: %s) ---", authKey)
// Loop to send 20 readings
for i := 0; i < 20; i++ {
// Simulate random weather data reading
temp := 20.0 + rand.Float64()*25.0 // Temperature between 20 and 45
// 🚨 Alert Test (Status Error)
// At reading 10, deliberately raise the temperature above the limit (45) to test server error handling.
if i == 10 && authKey == VALID_AUTH_KEY {
temp = 50.0
log.Printf("🚨 Sending deliberately high reading to test alert: %.2f C", temp)
}
data := &pb.WeatherData{
TemperatureCelsius: temp,
HumidityPercent: 40.0 + rand.Float64()*40.0,
PressureHpa: 980.0 + rand.Float64()*50.0,
Timestamp: time.Now().Unix(),
StationId: stationID,
}
if err := stream.Send(data); err != nil {
// Connection will be cut here if authentication failed or server aborted
log.Printf("❌ Failed to send data #%d: %v", i, err)
break
}
if i < 19 {
time.Sleep(500 * time.Millisecond) // Wait 0.5 seconds between readings
}
}
// 2. Close the stream and receive final statistics
stats, err := stream.CloseAndRecv()
// 3. Examine the response (including alerts and errors)
if err != nil {
st, ok := status.FromError(err)
if ok {
// Check for Authentication Error (UNAUTHENTICATED)
if st.Code() == codes.Unauthenticated {
log.Printf("❌ Failed Auth Test Successful: Status Code = %v, Message = %s", st.Code(), st.Message())
} else if st.Code() == codes.ResourceExhausted { // << FIX APPLIED HERE
log.Printf("🔔 Alert Test Successful (Boundary Exceeded): Status Code = %v, Message = %s", st.Code(), st.Message())
} else {
log.Printf("❌ Failed to close stream or unexpected error: %v", err)
}
} else {
log.Fatalf("❌ Failed to close stream: %v", err)
}
return
}
// If connection succeeded and no alert occurred (i.e., didn't send 50C)
log.Printf("✅ Transmission successful. Final statistics received:")
log.Printf(" > Average Temperature: %.2f C", stats.AvgTemperature)
log.Printf(" > Report Time: %v", time.Unix(stats.ReportTime, 0).Format(time.Stamp))
}
func main() {
// Setup connection to the Analytics Server
conn, err := grpc.Dial(address, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("❌ Failed to connect to Analytics Server: %v", err)
}
defer conn.Close()
// Create gRPC client stub
client := pb.NewWeatherAnalyticsServiceClient(conn)
// --- 1. Run 1: Successful Auth Test with extreme data (to trigger alert) ---
log.Println("\n==============================================")
log.Println(" Run 1: Valid Key to Test Alert")
log.Println("==============================================")
streamWeatherData(client, VALID_AUTH_KEY) // The correct key
// --- 2. Run 2: Failed Authentication Test ---
log.Println("\n==============================================")
log.Println(" Run 2: Invalid Key to Test Auth")
log.Println("==============================================")
streamWeatherData(client, "INVALID_KEY") // The wrong key
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment