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 com.weather.monitoring.grpc;
import static io.grpc.MethodDescriptor.generateFullMethodName;
/**
* <pre>
* 2. Weather Analytics Service (Python) - The central server
* </pre>
*/
@javax.annotation.Generated(
value = "by gRPC proto compiler (version 1.68.0)",
comments = "Source: weather.proto")
@io.grpc.stub.annotations.GrpcGenerated
public final class WeatherAnalyticsServiceGrpc {
private WeatherAnalyticsServiceGrpc() {}
public static final java.lang.String SERVICE_NAME = "weather_system.WeatherAnalyticsService";
// Static method descriptors that strictly reflect the proto.
private static volatile io.grpc.MethodDescriptor<com.weather.monitoring.grpc.Weather.WeatherData,
com.weather.monitoring.grpc.Weather.WeatherStats> getStreamWeatherDataMethod;
@io.grpc.stub.annotations.RpcMethod(
fullMethodName = SERVICE_NAME + '/' + "StreamWeatherData",
requestType = com.weather.monitoring.grpc.Weather.WeatherData.class,
responseType = com.weather.monitoring.grpc.Weather.WeatherStats.class,
methodType = io.grpc.MethodDescriptor.MethodType.CLIENT_STREAMING)
public static io.grpc.MethodDescriptor<com.weather.monitoring.grpc.Weather.WeatherData,
com.weather.monitoring.grpc.Weather.WeatherStats> getStreamWeatherDataMethod() {
io.grpc.MethodDescriptor<com.weather.monitoring.grpc.Weather.WeatherData, com.weather.monitoring.grpc.Weather.WeatherStats> getStreamWeatherDataMethod;
if ((getStreamWeatherDataMethod = WeatherAnalyticsServiceGrpc.getStreamWeatherDataMethod) == null) {
synchronized (WeatherAnalyticsServiceGrpc.class) {
if ((getStreamWeatherDataMethod = WeatherAnalyticsServiceGrpc.getStreamWeatherDataMethod) == null) {
WeatherAnalyticsServiceGrpc.getStreamWeatherDataMethod = getStreamWeatherDataMethod =
io.grpc.MethodDescriptor.<com.weather.monitoring.grpc.Weather.WeatherData, com.weather.monitoring.grpc.Weather.WeatherStats>newBuilder()
.setType(io.grpc.MethodDescriptor.MethodType.CLIENT_STREAMING)
.setFullMethodName(generateFullMethodName(SERVICE_NAME, "StreamWeatherData"))
.setSampledToLocalTracing(true)
.setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
com.weather.monitoring.grpc.Weather.WeatherData.getDefaultInstance()))
.setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
com.weather.monitoring.grpc.Weather.WeatherStats.getDefaultInstance()))
.setSchemaDescriptor(new WeatherAnalyticsServiceMethodDescriptorSupplier("StreamWeatherData"))
.build();
}
}
}
return getStreamWeatherDataMethod;
}
private static volatile io.grpc.MethodDescriptor<com.weather.monitoring.grpc.Weather.Empty,
com.weather.monitoring.grpc.Weather.WeatherStats> getStreamAnalyticsMethod;
@io.grpc.stub.annotations.RpcMethod(
fullMethodName = SERVICE_NAME + '/' + "StreamAnalytics",
requestType = com.weather.monitoring.grpc.Weather.Empty.class,
responseType = com.weather.monitoring.grpc.Weather.WeatherStats.class,
methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING)
public static io.grpc.MethodDescriptor<com.weather.monitoring.grpc.Weather.Empty,
com.weather.monitoring.grpc.Weather.WeatherStats> getStreamAnalyticsMethod() {
io.grpc.MethodDescriptor<com.weather.monitoring.grpc.Weather.Empty, com.weather.monitoring.grpc.Weather.WeatherStats> getStreamAnalyticsMethod;
if ((getStreamAnalyticsMethod = WeatherAnalyticsServiceGrpc.getStreamAnalyticsMethod) == null) {
synchronized (WeatherAnalyticsServiceGrpc.class) {
if ((getStreamAnalyticsMethod = WeatherAnalyticsServiceGrpc.getStreamAnalyticsMethod) == null) {
WeatherAnalyticsServiceGrpc.getStreamAnalyticsMethod = getStreamAnalyticsMethod =
io.grpc.MethodDescriptor.<com.weather.monitoring.grpc.Weather.Empty, com.weather.monitoring.grpc.Weather.WeatherStats>newBuilder()
.setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING)
.setFullMethodName(generateFullMethodName(SERVICE_NAME, "StreamAnalytics"))
.setSampledToLocalTracing(true)
.setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
com.weather.monitoring.grpc.Weather.Empty.getDefaultInstance()))
.setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
com.weather.monitoring.grpc.Weather.WeatherStats.getDefaultInstance()))
.setSchemaDescriptor(new WeatherAnalyticsServiceMethodDescriptorSupplier("StreamAnalytics"))
.build();
}
}
}
return getStreamAnalyticsMethod;
}
private static volatile io.grpc.MethodDescriptor<com.weather.monitoring.grpc.Weather.Empty,
com.weather.monitoring.grpc.Weather.WeatherStats> getGetLastReportMethod;
@io.grpc.stub.annotations.RpcMethod(
fullMethodName = SERVICE_NAME + '/' + "GetLastReport",
requestType = com.weather.monitoring.grpc.Weather.Empty.class,
responseType = com.weather.monitoring.grpc.Weather.WeatherStats.class,
methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
public static io.grpc.MethodDescriptor<com.weather.monitoring.grpc.Weather.Empty,
com.weather.monitoring.grpc.Weather.WeatherStats> getGetLastReportMethod() {
io.grpc.MethodDescriptor<com.weather.monitoring.grpc.Weather.Empty, com.weather.monitoring.grpc.Weather.WeatherStats> getGetLastReportMethod;
if ((getGetLastReportMethod = WeatherAnalyticsServiceGrpc.getGetLastReportMethod) == null) {
synchronized (WeatherAnalyticsServiceGrpc.class) {
if ((getGetLastReportMethod = WeatherAnalyticsServiceGrpc.getGetLastReportMethod) == null) {
WeatherAnalyticsServiceGrpc.getGetLastReportMethod = getGetLastReportMethod =
io.grpc.MethodDescriptor.<com.weather.monitoring.grpc.Weather.Empty, com.weather.monitoring.grpc.Weather.WeatherStats>newBuilder()
.setType(io.grpc.MethodDescriptor.MethodType.UNARY)
.setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetLastReport"))
.setSampledToLocalTracing(true)
.setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
com.weather.monitoring.grpc.Weather.Empty.getDefaultInstance()))
.setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
com.weather.monitoring.grpc.Weather.WeatherStats.getDefaultInstance()))
.setSchemaDescriptor(new WeatherAnalyticsServiceMethodDescriptorSupplier("GetLastReport"))
.build();
}
}
}
return getGetLastReportMethod;
}
private static volatile io.grpc.MethodDescriptor<com.weather.monitoring.grpc.Weather.AlertMessage,
com.weather.monitoring.grpc.Weather.WeatherData> getControlSensorMethod;
@io.grpc.stub.annotations.RpcMethod(
fullMethodName = SERVICE_NAME + '/' + "ControlSensor",
requestType = com.weather.monitoring.grpc.Weather.AlertMessage.class,
responseType = com.weather.monitoring.grpc.Weather.WeatherData.class,
methodType = io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING)
public static io.grpc.MethodDescriptor<com.weather.monitoring.grpc.Weather.AlertMessage,
com.weather.monitoring.grpc.Weather.WeatherData> getControlSensorMethod() {
io.grpc.MethodDescriptor<com.weather.monitoring.grpc.Weather.AlertMessage, com.weather.monitoring.grpc.Weather.WeatherData> getControlSensorMethod;
if ((getControlSensorMethod = WeatherAnalyticsServiceGrpc.getControlSensorMethod) == null) {
synchronized (WeatherAnalyticsServiceGrpc.class) {
if ((getControlSensorMethod = WeatherAnalyticsServiceGrpc.getControlSensorMethod) == null) {
WeatherAnalyticsServiceGrpc.getControlSensorMethod = getControlSensorMethod =
io.grpc.MethodDescriptor.<com.weather.monitoring.grpc.Weather.AlertMessage, com.weather.monitoring.grpc.Weather.WeatherData>newBuilder()
.setType(io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING)
.setFullMethodName(generateFullMethodName(SERVICE_NAME, "ControlSensor"))
.setSampledToLocalTracing(true)
.setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
com.weather.monitoring.grpc.Weather.AlertMessage.getDefaultInstance()))
.setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
com.weather.monitoring.grpc.Weather.WeatherData.getDefaultInstance()))
.setSchemaDescriptor(new WeatherAnalyticsServiceMethodDescriptorSupplier("ControlSensor"))
.build();
}
}
}
return getControlSensorMethod;
}
/**
* Creates a new async stub that supports all call types for the service
*/
public static WeatherAnalyticsServiceStub newStub(io.grpc.Channel channel) {
io.grpc.stub.AbstractStub.StubFactory<WeatherAnalyticsServiceStub> factory =
new io.grpc.stub.AbstractStub.StubFactory<WeatherAnalyticsServiceStub>() {
@java.lang.Override
public WeatherAnalyticsServiceStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new WeatherAnalyticsServiceStub(channel, callOptions);
}
};
return WeatherAnalyticsServiceStub.newStub(factory, channel);
}
/**
* Creates a new blocking-style stub that supports unary and streaming output calls on the service
*/
public static WeatherAnalyticsServiceBlockingStub newBlockingStub(
io.grpc.Channel channel) {
io.grpc.stub.AbstractStub.StubFactory<WeatherAnalyticsServiceBlockingStub> factory =
new io.grpc.stub.AbstractStub.StubFactory<WeatherAnalyticsServiceBlockingStub>() {
@java.lang.Override
public WeatherAnalyticsServiceBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new WeatherAnalyticsServiceBlockingStub(channel, callOptions);
}
};
return WeatherAnalyticsServiceBlockingStub.newStub(factory, channel);
}
/**
* Creates a new ListenableFuture-style stub that supports unary calls on the service
*/
public static WeatherAnalyticsServiceFutureStub newFutureStub(
io.grpc.Channel channel) {
io.grpc.stub.AbstractStub.StubFactory<WeatherAnalyticsServiceFutureStub> factory =
new io.grpc.stub.AbstractStub.StubFactory<WeatherAnalyticsServiceFutureStub>() {
@java.lang.Override
public WeatherAnalyticsServiceFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new WeatherAnalyticsServiceFutureStub(channel, callOptions);
}
};
return WeatherAnalyticsServiceFutureStub.newStub(factory, channel);
}
/**
* <pre>
* 2. Weather Analytics Service (Python) - The central server
* </pre>
*/
public interface AsyncService {
/**
* <pre>
* 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.
* </pre>
*/
default io.grpc.stub.StreamObserver<com.weather.monitoring.grpc.Weather.WeatherData> streamWeatherData(
io.grpc.stub.StreamObserver<com.weather.monitoring.grpc.Weather.WeatherStats> responseObserver) {
return io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall(getStreamWeatherDataMethod(), responseObserver);
}
/**
* <pre>
* B. Server Streaming (Required: Send to Dashboard)
* Dashboard requests (with Empty) a continuous stream of reports.
* </pre>
*/
default void streamAnalytics(com.weather.monitoring.grpc.Weather.Empty request,
io.grpc.stub.StreamObserver<com.weather.monitoring.grpc.Weather.WeatherStats> responseObserver) {
io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getStreamAnalyticsMethod(), responseObserver);
}
/**
* <pre>
* C. Unary RPC (Optional Requirement: Retrieve last report)
* Dashboard manually requests the last stored stats.
* </pre>
*/
default void getLastReport(com.weather.monitoring.grpc.Weather.Empty request,
io.grpc.stub.StreamObserver<com.weather.monitoring.grpc.Weather.WeatherStats> responseObserver) {
io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetLastReportMethod(), responseObserver);
}
/**
* <pre>
* D. Bidirectional Streaming (Academic addition)
* Allows the server to send immediate alerts and potentially control the sensor.
* </pre>
*/
default io.grpc.stub.StreamObserver<com.weather.monitoring.grpc.Weather.AlertMessage> controlSensor(
io.grpc.stub.StreamObserver<com.weather.monitoring.grpc.Weather.WeatherData> responseObserver) {
return io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall(getControlSensorMethod(), responseObserver);
}
}
/**
* Base class for the server implementation of the service WeatherAnalyticsService.
* <pre>
* 2. Weather Analytics Service (Python) - The central server
* </pre>
*/
public static abstract class WeatherAnalyticsServiceImplBase
implements io.grpc.BindableService, AsyncService {
@java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
return WeatherAnalyticsServiceGrpc.bindService(this);
}
}
/**
* A stub to allow clients to do asynchronous rpc calls to service WeatherAnalyticsService.
* <pre>
* 2. Weather Analytics Service (Python) - The central server
* </pre>
*/
public static final class WeatherAnalyticsServiceStub
extends io.grpc.stub.AbstractAsyncStub<WeatherAnalyticsServiceStub> {
private WeatherAnalyticsServiceStub(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
super(channel, callOptions);
}
@java.lang.Override
protected WeatherAnalyticsServiceStub build(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new WeatherAnalyticsServiceStub(channel, callOptions);
}
/**
* <pre>
* 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.
* </pre>
*/
public io.grpc.stub.StreamObserver<com.weather.monitoring.grpc.Weather.WeatherData> streamWeatherData(
io.grpc.stub.StreamObserver<com.weather.monitoring.grpc.Weather.WeatherStats> responseObserver) {
return io.grpc.stub.ClientCalls.asyncClientStreamingCall(
getChannel().newCall(getStreamWeatherDataMethod(), getCallOptions()), responseObserver);
}
/**
* <pre>
* B. Server Streaming (Required: Send to Dashboard)
* Dashboard requests (with Empty) a continuous stream of reports.
* </pre>
*/
public void streamAnalytics(com.weather.monitoring.grpc.Weather.Empty request,
io.grpc.stub.StreamObserver<com.weather.monitoring.grpc.Weather.WeatherStats> responseObserver) {
io.grpc.stub.ClientCalls.asyncServerStreamingCall(
getChannel().newCall(getStreamAnalyticsMethod(), getCallOptions()), request, responseObserver);
}
/**
* <pre>
* C. Unary RPC (Optional Requirement: Retrieve last report)
* Dashboard manually requests the last stored stats.
* </pre>
*/
public void getLastReport(com.weather.monitoring.grpc.Weather.Empty request,
io.grpc.stub.StreamObserver<com.weather.monitoring.grpc.Weather.WeatherStats> responseObserver) {
io.grpc.stub.ClientCalls.asyncUnaryCall(
getChannel().newCall(getGetLastReportMethod(), getCallOptions()), request, responseObserver);
}
/**
* <pre>
* D. Bidirectional Streaming (Academic addition)
* Allows the server to send immediate alerts and potentially control the sensor.
* </pre>
*/
public io.grpc.stub.StreamObserver<com.weather.monitoring.grpc.Weather.AlertMessage> controlSensor(
io.grpc.stub.StreamObserver<com.weather.monitoring.grpc.Weather.WeatherData> responseObserver) {
return io.grpc.stub.ClientCalls.asyncBidiStreamingCall(
getChannel().newCall(getControlSensorMethod(), getCallOptions()), responseObserver);
}
}
/**
* A stub to allow clients to do synchronous rpc calls to service WeatherAnalyticsService.
* <pre>
* 2. Weather Analytics Service (Python) - The central server
* </pre>
*/
public static final class WeatherAnalyticsServiceBlockingStub
extends io.grpc.stub.AbstractBlockingStub<WeatherAnalyticsServiceBlockingStub> {
private WeatherAnalyticsServiceBlockingStub(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
super(channel, callOptions);
}
@java.lang.Override
protected WeatherAnalyticsServiceBlockingStub build(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new WeatherAnalyticsServiceBlockingStub(channel, callOptions);
}
/**
* <pre>
* B. Server Streaming (Required: Send to Dashboard)
* Dashboard requests (with Empty) a continuous stream of reports.
* </pre>
*/
public java.util.Iterator<com.weather.monitoring.grpc.Weather.WeatherStats> streamAnalytics(
com.weather.monitoring.grpc.Weather.Empty request) {
return io.grpc.stub.ClientCalls.blockingServerStreamingCall(
getChannel(), getStreamAnalyticsMethod(), getCallOptions(), request);
}
/**
* <pre>
* C. Unary RPC (Optional Requirement: Retrieve last report)
* Dashboard manually requests the last stored stats.
* </pre>
*/
public com.weather.monitoring.grpc.Weather.WeatherStats getLastReport(com.weather.monitoring.grpc.Weather.Empty request) {
return io.grpc.stub.ClientCalls.blockingUnaryCall(
getChannel(), getGetLastReportMethod(), getCallOptions(), request);
}
}
/**
* A stub to allow clients to do ListenableFuture-style rpc calls to service WeatherAnalyticsService.
* <pre>
* 2. Weather Analytics Service (Python) - The central server
* </pre>
*/
public static final class WeatherAnalyticsServiceFutureStub
extends io.grpc.stub.AbstractFutureStub<WeatherAnalyticsServiceFutureStub> {
private WeatherAnalyticsServiceFutureStub(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
super(channel, callOptions);
}
@java.lang.Override
protected WeatherAnalyticsServiceFutureStub build(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new WeatherAnalyticsServiceFutureStub(channel, callOptions);
}
/**
* <pre>
* C. Unary RPC (Optional Requirement: Retrieve last report)
* Dashboard manually requests the last stored stats.
* </pre>
*/
public com.google.common.util.concurrent.ListenableFuture<com.weather.monitoring.grpc.Weather.WeatherStats> getLastReport(
com.weather.monitoring.grpc.Weather.Empty request) {
return io.grpc.stub.ClientCalls.futureUnaryCall(
getChannel().newCall(getGetLastReportMethod(), getCallOptions()), request);
}
}
private static final int METHODID_STREAM_ANALYTICS = 0;
private static final int METHODID_GET_LAST_REPORT = 1;
private static final int METHODID_STREAM_WEATHER_DATA = 2;
private static final int METHODID_CONTROL_SENSOR = 3;
private static final class MethodHandlers<Req, Resp> implements
io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
io.grpc.stub.ServerCalls.ServerStreamingMethod<Req, Resp>,
io.grpc.stub.ServerCalls.ClientStreamingMethod<Req, Resp>,
io.grpc.stub.ServerCalls.BidiStreamingMethod<Req, Resp> {
private final AsyncService serviceImpl;
private final int methodId;
MethodHandlers(AsyncService serviceImpl, int methodId) {
this.serviceImpl = serviceImpl;
this.methodId = methodId;
}
@java.lang.Override
@java.lang.SuppressWarnings("unchecked")
public void invoke(Req request, io.grpc.stub.StreamObserver<Resp> responseObserver) {
switch (methodId) {
case METHODID_STREAM_ANALYTICS:
serviceImpl.streamAnalytics((com.weather.monitoring.grpc.Weather.Empty) request,
(io.grpc.stub.StreamObserver<com.weather.monitoring.grpc.Weather.WeatherStats>) responseObserver);
break;
case METHODID_GET_LAST_REPORT:
serviceImpl.getLastReport((com.weather.monitoring.grpc.Weather.Empty) request,
(io.grpc.stub.StreamObserver<com.weather.monitoring.grpc.Weather.WeatherStats>) responseObserver);
break;
default:
throw new AssertionError();
}
}
@java.lang.Override
@java.lang.SuppressWarnings("unchecked")
public io.grpc.stub.StreamObserver<Req> invoke(
io.grpc.stub.StreamObserver<Resp> responseObserver) {
switch (methodId) {
case METHODID_STREAM_WEATHER_DATA:
return (io.grpc.stub.StreamObserver<Req>) serviceImpl.streamWeatherData(
(io.grpc.stub.StreamObserver<com.weather.monitoring.grpc.Weather.WeatherStats>) responseObserver);
case METHODID_CONTROL_SENSOR:
return (io.grpc.stub.StreamObserver<Req>) serviceImpl.controlSensor(
(io.grpc.stub.StreamObserver<com.weather.monitoring.grpc.Weather.WeatherData>) responseObserver);
default:
throw new AssertionError();
}
}
}
public static final io.grpc.ServerServiceDefinition bindService(AsyncService service) {
return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
.addMethod(
getStreamWeatherDataMethod(),
io.grpc.stub.ServerCalls.asyncClientStreamingCall(
new MethodHandlers<
com.weather.monitoring.grpc.Weather.WeatherData,
com.weather.monitoring.grpc.Weather.WeatherStats>(
service, METHODID_STREAM_WEATHER_DATA)))
.addMethod(
getStreamAnalyticsMethod(),
io.grpc.stub.ServerCalls.asyncServerStreamingCall(
new MethodHandlers<
com.weather.monitoring.grpc.Weather.Empty,
com.weather.monitoring.grpc.Weather.WeatherStats>(
service, METHODID_STREAM_ANALYTICS)))
.addMethod(
getGetLastReportMethod(),
io.grpc.stub.ServerCalls.asyncUnaryCall(
new MethodHandlers<
com.weather.monitoring.grpc.Weather.Empty,
com.weather.monitoring.grpc.Weather.WeatherStats>(
service, METHODID_GET_LAST_REPORT)))
.addMethod(
getControlSensorMethod(),
io.grpc.stub.ServerCalls.asyncBidiStreamingCall(
new MethodHandlers<
com.weather.monitoring.grpc.Weather.AlertMessage,
com.weather.monitoring.grpc.Weather.WeatherData>(
service, METHODID_CONTROL_SENSOR)))
.build();
}
private static abstract class WeatherAnalyticsServiceBaseDescriptorSupplier
implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier {
WeatherAnalyticsServiceBaseDescriptorSupplier() {}
@java.lang.Override
public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() {
return com.weather.monitoring.grpc.Weather.getDescriptor();
}
@java.lang.Override
public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() {
return getFileDescriptor().findServiceByName("WeatherAnalyticsService");
}
}
private static final class WeatherAnalyticsServiceFileDescriptorSupplier
extends WeatherAnalyticsServiceBaseDescriptorSupplier {
WeatherAnalyticsServiceFileDescriptorSupplier() {}
}
private static final class WeatherAnalyticsServiceMethodDescriptorSupplier
extends WeatherAnalyticsServiceBaseDescriptorSupplier
implements io.grpc.protobuf.ProtoMethodDescriptorSupplier {
private final java.lang.String methodName;
WeatherAnalyticsServiceMethodDescriptorSupplier(java.lang.String methodName) {
this.methodName = methodName;
}
@java.lang.Override
public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() {
return getServiceDescriptor().findMethodByName(methodName);
}
}
private static volatile io.grpc.ServiceDescriptor serviceDescriptor;
public static io.grpc.ServiceDescriptor getServiceDescriptor() {
io.grpc.ServiceDescriptor result = serviceDescriptor;
if (result == null) {
synchronized (WeatherAnalyticsServiceGrpc.class) {
result = serviceDescriptor;
if (result == null) {
serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME)
.setSchemaDescriptor(new WeatherAnalyticsServiceFileDescriptorSupplier())
.addMethod(getStreamWeatherDataMethod())
.addMethod(getStreamAnalyticsMethod())
.addMethod(getGetLastReportMethod())
.addMethod(getControlSensorMethod())
.build();
}
}
}
return result;
}
}
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 source diff could not be displayed because it is too large. You can view the blob instead.
package weather_system;
import static io.grpc.MethodDescriptor.generateFullMethodName;
/**
* <pre>
* 2. Weather Analytics Service (Python) - The central server
* </pre>
*/
@javax.annotation.Generated(
value = "by gRPC proto compiler (version 1.68.0)",
comments = "Source: weather.proto")
@io.grpc.stub.annotations.GrpcGenerated
public final class WeatherAnalyticsServiceGrpc {
private WeatherAnalyticsServiceGrpc() {}
public static final java.lang.String SERVICE_NAME = "weather_system.WeatherAnalyticsService";
// Static method descriptors that strictly reflect the proto.
private static volatile io.grpc.MethodDescriptor<weather_system.Weather.WeatherData,
weather_system.Weather.WeatherStats> getStreamWeatherDataMethod;
@io.grpc.stub.annotations.RpcMethod(
fullMethodName = SERVICE_NAME + '/' + "StreamWeatherData",
requestType = weather_system.Weather.WeatherData.class,
responseType = weather_system.Weather.WeatherStats.class,
methodType = io.grpc.MethodDescriptor.MethodType.CLIENT_STREAMING)
public static io.grpc.MethodDescriptor<weather_system.Weather.WeatherData,
weather_system.Weather.WeatherStats> getStreamWeatherDataMethod() {
io.grpc.MethodDescriptor<weather_system.Weather.WeatherData, weather_system.Weather.WeatherStats> getStreamWeatherDataMethod;
if ((getStreamWeatherDataMethod = WeatherAnalyticsServiceGrpc.getStreamWeatherDataMethod) == null) {
synchronized (WeatherAnalyticsServiceGrpc.class) {
if ((getStreamWeatherDataMethod = WeatherAnalyticsServiceGrpc.getStreamWeatherDataMethod) == null) {
WeatherAnalyticsServiceGrpc.getStreamWeatherDataMethod = getStreamWeatherDataMethod =
io.grpc.MethodDescriptor.<weather_system.Weather.WeatherData, weather_system.Weather.WeatherStats>newBuilder()
.setType(io.grpc.MethodDescriptor.MethodType.CLIENT_STREAMING)
.setFullMethodName(generateFullMethodName(SERVICE_NAME, "StreamWeatherData"))
.setSampledToLocalTracing(true)
.setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
weather_system.Weather.WeatherData.getDefaultInstance()))
.setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
weather_system.Weather.WeatherStats.getDefaultInstance()))
.setSchemaDescriptor(new WeatherAnalyticsServiceMethodDescriptorSupplier("StreamWeatherData"))
.build();
}
}
}
return getStreamWeatherDataMethod;
}
private static volatile io.grpc.MethodDescriptor<weather_system.Weather.Empty,
weather_system.Weather.WeatherStats> getStreamAnalyticsMethod;
@io.grpc.stub.annotations.RpcMethod(
fullMethodName = SERVICE_NAME + '/' + "StreamAnalytics",
requestType = weather_system.Weather.Empty.class,
responseType = weather_system.Weather.WeatherStats.class,
methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING)
public static io.grpc.MethodDescriptor<weather_system.Weather.Empty,
weather_system.Weather.WeatherStats> getStreamAnalyticsMethod() {
io.grpc.MethodDescriptor<weather_system.Weather.Empty, weather_system.Weather.WeatherStats> getStreamAnalyticsMethod;
if ((getStreamAnalyticsMethod = WeatherAnalyticsServiceGrpc.getStreamAnalyticsMethod) == null) {
synchronized (WeatherAnalyticsServiceGrpc.class) {
if ((getStreamAnalyticsMethod = WeatherAnalyticsServiceGrpc.getStreamAnalyticsMethod) == null) {
WeatherAnalyticsServiceGrpc.getStreamAnalyticsMethod = getStreamAnalyticsMethod =
io.grpc.MethodDescriptor.<weather_system.Weather.Empty, weather_system.Weather.WeatherStats>newBuilder()
.setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING)
.setFullMethodName(generateFullMethodName(SERVICE_NAME, "StreamAnalytics"))
.setSampledToLocalTracing(true)
.setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
weather_system.Weather.Empty.getDefaultInstance()))
.setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
weather_system.Weather.WeatherStats.getDefaultInstance()))
.setSchemaDescriptor(new WeatherAnalyticsServiceMethodDescriptorSupplier("StreamAnalytics"))
.build();
}
}
}
return getStreamAnalyticsMethod;
}
private static volatile io.grpc.MethodDescriptor<weather_system.Weather.Empty,
weather_system.Weather.WeatherStats> getGetLastReportMethod;
@io.grpc.stub.annotations.RpcMethod(
fullMethodName = SERVICE_NAME + '/' + "GetLastReport",
requestType = weather_system.Weather.Empty.class,
responseType = weather_system.Weather.WeatherStats.class,
methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
public static io.grpc.MethodDescriptor<weather_system.Weather.Empty,
weather_system.Weather.WeatherStats> getGetLastReportMethod() {
io.grpc.MethodDescriptor<weather_system.Weather.Empty, weather_system.Weather.WeatherStats> getGetLastReportMethod;
if ((getGetLastReportMethod = WeatherAnalyticsServiceGrpc.getGetLastReportMethod) == null) {
synchronized (WeatherAnalyticsServiceGrpc.class) {
if ((getGetLastReportMethod = WeatherAnalyticsServiceGrpc.getGetLastReportMethod) == null) {
WeatherAnalyticsServiceGrpc.getGetLastReportMethod = getGetLastReportMethod =
io.grpc.MethodDescriptor.<weather_system.Weather.Empty, weather_system.Weather.WeatherStats>newBuilder()
.setType(io.grpc.MethodDescriptor.MethodType.UNARY)
.setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetLastReport"))
.setSampledToLocalTracing(true)
.setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
weather_system.Weather.Empty.getDefaultInstance()))
.setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
weather_system.Weather.WeatherStats.getDefaultInstance()))
.setSchemaDescriptor(new WeatherAnalyticsServiceMethodDescriptorSupplier("GetLastReport"))
.build();
}
}
}
return getGetLastReportMethod;
}
private static volatile io.grpc.MethodDescriptor<weather_system.Weather.AlertMessage,
weather_system.Weather.WeatherData> getControlSensorMethod;
@io.grpc.stub.annotations.RpcMethod(
fullMethodName = SERVICE_NAME + '/' + "ControlSensor",
requestType = weather_system.Weather.AlertMessage.class,
responseType = weather_system.Weather.WeatherData.class,
methodType = io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING)
public static io.grpc.MethodDescriptor<weather_system.Weather.AlertMessage,
weather_system.Weather.WeatherData> getControlSensorMethod() {
io.grpc.MethodDescriptor<weather_system.Weather.AlertMessage, weather_system.Weather.WeatherData> getControlSensorMethod;
if ((getControlSensorMethod = WeatherAnalyticsServiceGrpc.getControlSensorMethod) == null) {
synchronized (WeatherAnalyticsServiceGrpc.class) {
if ((getControlSensorMethod = WeatherAnalyticsServiceGrpc.getControlSensorMethod) == null) {
WeatherAnalyticsServiceGrpc.getControlSensorMethod = getControlSensorMethod =
io.grpc.MethodDescriptor.<weather_system.Weather.AlertMessage, weather_system.Weather.WeatherData>newBuilder()
.setType(io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING)
.setFullMethodName(generateFullMethodName(SERVICE_NAME, "ControlSensor"))
.setSampledToLocalTracing(true)
.setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
weather_system.Weather.AlertMessage.getDefaultInstance()))
.setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
weather_system.Weather.WeatherData.getDefaultInstance()))
.setSchemaDescriptor(new WeatherAnalyticsServiceMethodDescriptorSupplier("ControlSensor"))
.build();
}
}
}
return getControlSensorMethod;
}
/**
* Creates a new async stub that supports all call types for the service
*/
public static WeatherAnalyticsServiceStub newStub(io.grpc.Channel channel) {
io.grpc.stub.AbstractStub.StubFactory<WeatherAnalyticsServiceStub> factory =
new io.grpc.stub.AbstractStub.StubFactory<WeatherAnalyticsServiceStub>() {
@java.lang.Override
public WeatherAnalyticsServiceStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new WeatherAnalyticsServiceStub(channel, callOptions);
}
};
return WeatherAnalyticsServiceStub.newStub(factory, channel);
}
/**
* Creates a new blocking-style stub that supports unary and streaming output calls on the service
*/
public static WeatherAnalyticsServiceBlockingStub newBlockingStub(
io.grpc.Channel channel) {
io.grpc.stub.AbstractStub.StubFactory<WeatherAnalyticsServiceBlockingStub> factory =
new io.grpc.stub.AbstractStub.StubFactory<WeatherAnalyticsServiceBlockingStub>() {
@java.lang.Override
public WeatherAnalyticsServiceBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new WeatherAnalyticsServiceBlockingStub(channel, callOptions);
}
};
return WeatherAnalyticsServiceBlockingStub.newStub(factory, channel);
}
/**
* Creates a new ListenableFuture-style stub that supports unary calls on the service
*/
public static WeatherAnalyticsServiceFutureStub newFutureStub(
io.grpc.Channel channel) {
io.grpc.stub.AbstractStub.StubFactory<WeatherAnalyticsServiceFutureStub> factory =
new io.grpc.stub.AbstractStub.StubFactory<WeatherAnalyticsServiceFutureStub>() {
@java.lang.Override
public WeatherAnalyticsServiceFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new WeatherAnalyticsServiceFutureStub(channel, callOptions);
}
};
return WeatherAnalyticsServiceFutureStub.newStub(factory, channel);
}
/**
* <pre>
* 2. Weather Analytics Service (Python) - The central server
* </pre>
*/
public interface AsyncService {
/**
* <pre>
* 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.
* </pre>
*/
default io.grpc.stub.StreamObserver<weather_system.Weather.WeatherData> streamWeatherData(
io.grpc.stub.StreamObserver<weather_system.Weather.WeatherStats> responseObserver) {
return io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall(getStreamWeatherDataMethod(), responseObserver);
}
/**
* <pre>
* B. Server Streaming (Required: Send to Dashboard)
* Dashboard requests (with Empty) a continuous stream of reports.
* </pre>
*/
default void streamAnalytics(weather_system.Weather.Empty request,
io.grpc.stub.StreamObserver<weather_system.Weather.WeatherStats> responseObserver) {
io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getStreamAnalyticsMethod(), responseObserver);
}
/**
* <pre>
* C. Unary RPC (Optional Requirement: Retrieve last report)
* Dashboard manually requests the last stored stats.
* </pre>
*/
default void getLastReport(weather_system.Weather.Empty request,
io.grpc.stub.StreamObserver<weather_system.Weather.WeatherStats> responseObserver) {
io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetLastReportMethod(), responseObserver);
}
/**
* <pre>
* D. Bidirectional Streaming (Academic addition)
* Allows the server to send immediate alerts and potentially control the sensor.
* </pre>
*/
default io.grpc.stub.StreamObserver<weather_system.Weather.AlertMessage> controlSensor(
io.grpc.stub.StreamObserver<weather_system.Weather.WeatherData> responseObserver) {
return io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall(getControlSensorMethod(), responseObserver);
}
}
/**
* Base class for the server implementation of the service WeatherAnalyticsService.
* <pre>
* 2. Weather Analytics Service (Python) - The central server
* </pre>
*/
public static abstract class WeatherAnalyticsServiceImplBase
implements io.grpc.BindableService, AsyncService {
@java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
return WeatherAnalyticsServiceGrpc.bindService(this);
}
}
/**
* A stub to allow clients to do asynchronous rpc calls to service WeatherAnalyticsService.
* <pre>
* 2. Weather Analytics Service (Python) - The central server
* </pre>
*/
public static final class WeatherAnalyticsServiceStub
extends io.grpc.stub.AbstractAsyncStub<WeatherAnalyticsServiceStub> {
private WeatherAnalyticsServiceStub(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
super(channel, callOptions);
}
@java.lang.Override
protected WeatherAnalyticsServiceStub build(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new WeatherAnalyticsServiceStub(channel, callOptions);
}
/**
* <pre>
* 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.
* </pre>
*/
public io.grpc.stub.StreamObserver<weather_system.Weather.WeatherData> streamWeatherData(
io.grpc.stub.StreamObserver<weather_system.Weather.WeatherStats> responseObserver) {
return io.grpc.stub.ClientCalls.asyncClientStreamingCall(
getChannel().newCall(getStreamWeatherDataMethod(), getCallOptions()), responseObserver);
}
/**
* <pre>
* B. Server Streaming (Required: Send to Dashboard)
* Dashboard requests (with Empty) a continuous stream of reports.
* </pre>
*/
public void streamAnalytics(weather_system.Weather.Empty request,
io.grpc.stub.StreamObserver<weather_system.Weather.WeatherStats> responseObserver) {
io.grpc.stub.ClientCalls.asyncServerStreamingCall(
getChannel().newCall(getStreamAnalyticsMethod(), getCallOptions()), request, responseObserver);
}
/**
* <pre>
* C. Unary RPC (Optional Requirement: Retrieve last report)
* Dashboard manually requests the last stored stats.
* </pre>
*/
public void getLastReport(weather_system.Weather.Empty request,
io.grpc.stub.StreamObserver<weather_system.Weather.WeatherStats> responseObserver) {
io.grpc.stub.ClientCalls.asyncUnaryCall(
getChannel().newCall(getGetLastReportMethod(), getCallOptions()), request, responseObserver);
}
/**
* <pre>
* D. Bidirectional Streaming (Academic addition)
* Allows the server to send immediate alerts and potentially control the sensor.
* </pre>
*/
public io.grpc.stub.StreamObserver<weather_system.Weather.AlertMessage> controlSensor(
io.grpc.stub.StreamObserver<weather_system.Weather.WeatherData> responseObserver) {
return io.grpc.stub.ClientCalls.asyncBidiStreamingCall(
getChannel().newCall(getControlSensorMethod(), getCallOptions()), responseObserver);
}
}
/**
* A stub to allow clients to do synchronous rpc calls to service WeatherAnalyticsService.
* <pre>
* 2. Weather Analytics Service (Python) - The central server
* </pre>
*/
public static final class WeatherAnalyticsServiceBlockingStub
extends io.grpc.stub.AbstractBlockingStub<WeatherAnalyticsServiceBlockingStub> {
private WeatherAnalyticsServiceBlockingStub(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
super(channel, callOptions);
}
@java.lang.Override
protected WeatherAnalyticsServiceBlockingStub build(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new WeatherAnalyticsServiceBlockingStub(channel, callOptions);
}
/**
* <pre>
* B. Server Streaming (Required: Send to Dashboard)
* Dashboard requests (with Empty) a continuous stream of reports.
* </pre>
*/
public java.util.Iterator<weather_system.Weather.WeatherStats> streamAnalytics(
weather_system.Weather.Empty request) {
return io.grpc.stub.ClientCalls.blockingServerStreamingCall(
getChannel(), getStreamAnalyticsMethod(), getCallOptions(), request);
}
/**
* <pre>
* C. Unary RPC (Optional Requirement: Retrieve last report)
* Dashboard manually requests the last stored stats.
* </pre>
*/
public weather_system.Weather.WeatherStats getLastReport(weather_system.Weather.Empty request) {
return io.grpc.stub.ClientCalls.blockingUnaryCall(
getChannel(), getGetLastReportMethod(), getCallOptions(), request);
}
}
/**
* A stub to allow clients to do ListenableFuture-style rpc calls to service WeatherAnalyticsService.
* <pre>
* 2. Weather Analytics Service (Python) - The central server
* </pre>
*/
public static final class WeatherAnalyticsServiceFutureStub
extends io.grpc.stub.AbstractFutureStub<WeatherAnalyticsServiceFutureStub> {
private WeatherAnalyticsServiceFutureStub(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
super(channel, callOptions);
}
@java.lang.Override
protected WeatherAnalyticsServiceFutureStub build(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new WeatherAnalyticsServiceFutureStub(channel, callOptions);
}
/**
* <pre>
* C. Unary RPC (Optional Requirement: Retrieve last report)
* Dashboard manually requests the last stored stats.
* </pre>
*/
public com.google.common.util.concurrent.ListenableFuture<weather_system.Weather.WeatherStats> getLastReport(
weather_system.Weather.Empty request) {
return io.grpc.stub.ClientCalls.futureUnaryCall(
getChannel().newCall(getGetLastReportMethod(), getCallOptions()), request);
}
}
private static final int METHODID_STREAM_ANALYTICS = 0;
private static final int METHODID_GET_LAST_REPORT = 1;
private static final int METHODID_STREAM_WEATHER_DATA = 2;
private static final int METHODID_CONTROL_SENSOR = 3;
private static final class MethodHandlers<Req, Resp> implements
io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
io.grpc.stub.ServerCalls.ServerStreamingMethod<Req, Resp>,
io.grpc.stub.ServerCalls.ClientStreamingMethod<Req, Resp>,
io.grpc.stub.ServerCalls.BidiStreamingMethod<Req, Resp> {
private final AsyncService serviceImpl;
private final int methodId;
MethodHandlers(AsyncService serviceImpl, int methodId) {
this.serviceImpl = serviceImpl;
this.methodId = methodId;
}
@java.lang.Override
@java.lang.SuppressWarnings("unchecked")
public void invoke(Req request, io.grpc.stub.StreamObserver<Resp> responseObserver) {
switch (methodId) {
case METHODID_STREAM_ANALYTICS:
serviceImpl.streamAnalytics((weather_system.Weather.Empty) request,
(io.grpc.stub.StreamObserver<weather_system.Weather.WeatherStats>) responseObserver);
break;
case METHODID_GET_LAST_REPORT:
serviceImpl.getLastReport((weather_system.Weather.Empty) request,
(io.grpc.stub.StreamObserver<weather_system.Weather.WeatherStats>) responseObserver);
break;
default:
throw new AssertionError();
}
}
@java.lang.Override
@java.lang.SuppressWarnings("unchecked")
public io.grpc.stub.StreamObserver<Req> invoke(
io.grpc.stub.StreamObserver<Resp> responseObserver) {
switch (methodId) {
case METHODID_STREAM_WEATHER_DATA:
return (io.grpc.stub.StreamObserver<Req>) serviceImpl.streamWeatherData(
(io.grpc.stub.StreamObserver<weather_system.Weather.WeatherStats>) responseObserver);
case METHODID_CONTROL_SENSOR:
return (io.grpc.stub.StreamObserver<Req>) serviceImpl.controlSensor(
(io.grpc.stub.StreamObserver<weather_system.Weather.WeatherData>) responseObserver);
default:
throw new AssertionError();
}
}
}
public static final io.grpc.ServerServiceDefinition bindService(AsyncService service) {
return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
.addMethod(
getStreamWeatherDataMethod(),
io.grpc.stub.ServerCalls.asyncClientStreamingCall(
new MethodHandlers<
weather_system.Weather.WeatherData,
weather_system.Weather.WeatherStats>(
service, METHODID_STREAM_WEATHER_DATA)))
.addMethod(
getStreamAnalyticsMethod(),
io.grpc.stub.ServerCalls.asyncServerStreamingCall(
new MethodHandlers<
weather_system.Weather.Empty,
weather_system.Weather.WeatherStats>(
service, METHODID_STREAM_ANALYTICS)))
.addMethod(
getGetLastReportMethod(),
io.grpc.stub.ServerCalls.asyncUnaryCall(
new MethodHandlers<
weather_system.Weather.Empty,
weather_system.Weather.WeatherStats>(
service, METHODID_GET_LAST_REPORT)))
.addMethod(
getControlSensorMethod(),
io.grpc.stub.ServerCalls.asyncBidiStreamingCall(
new MethodHandlers<
weather_system.Weather.AlertMessage,
weather_system.Weather.WeatherData>(
service, METHODID_CONTROL_SENSOR)))
.build();
}
private static abstract class WeatherAnalyticsServiceBaseDescriptorSupplier
implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier {
WeatherAnalyticsServiceBaseDescriptorSupplier() {}
@java.lang.Override
public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() {
return weather_system.Weather.getDescriptor();
}
@java.lang.Override
public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() {
return getFileDescriptor().findServiceByName("WeatherAnalyticsService");
}
}
private static final class WeatherAnalyticsServiceFileDescriptorSupplier
extends WeatherAnalyticsServiceBaseDescriptorSupplier {
WeatherAnalyticsServiceFileDescriptorSupplier() {}
}
private static final class WeatherAnalyticsServiceMethodDescriptorSupplier
extends WeatherAnalyticsServiceBaseDescriptorSupplier
implements io.grpc.protobuf.ProtoMethodDescriptorSupplier {
private final java.lang.String methodName;
WeatherAnalyticsServiceMethodDescriptorSupplier(java.lang.String methodName) {
this.methodName = methodName;
}
@java.lang.Override
public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() {
return getServiceDescriptor().findMethodByName(methodName);
}
}
private static volatile io.grpc.ServiceDescriptor serviceDescriptor;
public static io.grpc.ServiceDescriptor getServiceDescriptor() {
io.grpc.ServiceDescriptor result = serviceDescriptor;
if (result == null) {
synchronized (WeatherAnalyticsServiceGrpc.class) {
result = serviceDescriptor;
if (result == null) {
serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME)
.setSchemaDescriptor(new WeatherAnalyticsServiceFileDescriptorSupplier())
.addMethod(getStreamWeatherDataMethod())
.addMethod(getStreamAnalyticsMethod())
.addMethod(getGetLastReportMethod())
.addMethod(getControlSensorMethod())
.build();
}
}
}
return result;
}
}
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.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// The messages in this file describe the definitions found in .proto files.
// A valid .proto file can be translated directly to a FileDescriptorProto
// without any other information (e.g. without reading its imports).
syntax = "proto2";
package google.protobuf;
option go_package = "google.golang.org/protobuf/types/descriptorpb";
option java_package = "com.google.protobuf";
option java_outer_classname = "DescriptorProtos";
option csharp_namespace = "Google.Protobuf.Reflection";
option objc_class_prefix = "GPB";
option cc_enable_arenas = true;
// descriptor.proto must be optimized for speed because reflection-based
// algorithms don't work during bootstrapping.
option optimize_for = SPEED;
// The protocol compiler can output a FileDescriptorSet containing the .proto
// files it parses.
message FileDescriptorSet {
repeated FileDescriptorProto file = 1;
}
// The full set of known editions.
enum Edition {
// A placeholder for an unknown edition value.
EDITION_UNKNOWN = 0;
// A placeholder edition for specifying default behaviors *before* a feature
// was first introduced. This is effectively an "infinite past".
EDITION_LEGACY = 900;
// Legacy syntax "editions". These pre-date editions, but behave much like
// distinct editions. These can't be used to specify the edition of proto
// files, but feature definitions must supply proto2/proto3 defaults for
// backwards compatibility.
EDITION_PROTO2 = 998;
EDITION_PROTO3 = 999;
// Editions that have been released. The specific values are arbitrary and
// should not be depended on, but they will always be time-ordered for easy
// comparison.
EDITION_2023 = 1000;
EDITION_2024 = 1001;
// Placeholder editions for testing feature resolution. These should not be
// used or relyed on outside of tests.
EDITION_1_TEST_ONLY = 1;
EDITION_2_TEST_ONLY = 2;
EDITION_99997_TEST_ONLY = 99997;
EDITION_99998_TEST_ONLY = 99998;
EDITION_99999_TEST_ONLY = 99999;
// Placeholder for specifying unbounded edition support. This should only
// ever be used by plugins that can expect to never require any changes to
// support a new edition.
EDITION_MAX = 0x7FFFFFFF;
}
// Describes a complete .proto file.
message FileDescriptorProto {
optional string name = 1; // file name, relative to root of source tree
optional string package = 2; // e.g. "foo", "foo.bar", etc.
// Names of files imported by this file.
repeated string dependency = 3;
// Indexes of the public imported files in the dependency list above.
repeated int32 public_dependency = 10;
// Indexes of the weak imported files in the dependency list.
// For Google-internal migration only. Do not use.
repeated int32 weak_dependency = 11;
// All top-level definitions in this file.
repeated DescriptorProto message_type = 4;
repeated EnumDescriptorProto enum_type = 5;
repeated ServiceDescriptorProto service = 6;
repeated FieldDescriptorProto extension = 7;
optional FileOptions options = 8;
// This field contains optional information about the original source code.
// You may safely remove this entire field without harming runtime
// functionality of the descriptors -- the information is needed only by
// development tools.
optional SourceCodeInfo source_code_info = 9;
// The syntax of the proto file.
// The supported values are "proto2", "proto3", and "editions".
//
// If `edition` is present, this value must be "editions".
optional string syntax = 12;
// The edition of the proto file.
optional Edition edition = 14;
}
// Describes a message type.
message DescriptorProto {
optional string name = 1;
repeated FieldDescriptorProto field = 2;
repeated FieldDescriptorProto extension = 6;
repeated DescriptorProto nested_type = 3;
repeated EnumDescriptorProto enum_type = 4;
message ExtensionRange {
optional int32 start = 1; // Inclusive.
optional int32 end = 2; // Exclusive.
optional ExtensionRangeOptions options = 3;
}
repeated ExtensionRange extension_range = 5;
repeated OneofDescriptorProto oneof_decl = 8;
optional MessageOptions options = 7;
// Range of reserved tag numbers. Reserved tag numbers may not be used by
// fields or extension ranges in the same message. Reserved ranges may
// not overlap.
message ReservedRange {
optional int32 start = 1; // Inclusive.
optional int32 end = 2; // Exclusive.
}
repeated ReservedRange reserved_range = 9;
// Reserved field names, which may not be used by fields in the same message.
// A given name may only be reserved once.
repeated string reserved_name = 10;
}
message ExtensionRangeOptions {
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
message Declaration {
// The extension number declared within the extension range.
optional int32 number = 1;
// The fully-qualified name of the extension field. There must be a leading
// dot in front of the full name.
optional string full_name = 2;
// The fully-qualified type name of the extension field. Unlike
// Metadata.type, Declaration.type must have a leading dot for messages
// and enums.
optional string type = 3;
// If true, indicates that the number is reserved in the extension range,
// and any extension field with the number will fail to compile. Set this
// when a declared extension field is deleted.
optional bool reserved = 5;
// If true, indicates that the extension must be defined as repeated.
// Otherwise the extension must be defined as optional.
optional bool repeated = 6;
reserved 4; // removed is_repeated
}
// For external users: DO NOT USE. We are in the process of open sourcing
// extension declaration and executing internal cleanups before it can be
// used externally.
repeated Declaration declaration = 2 [retention = RETENTION_SOURCE];
// Any features defined in the specific edition.
optional FeatureSet features = 50;
// The verification state of the extension range.
enum VerificationState {
// All the extensions of the range must be declared.
DECLARATION = 0;
UNVERIFIED = 1;
}
// The verification state of the range.
// TODO: flip the default to DECLARATION once all empty ranges
// are marked as UNVERIFIED.
optional VerificationState verification = 3
[default = UNVERIFIED, retention = RETENTION_SOURCE];
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
// Describes a field within a message.
message FieldDescriptorProto {
enum Type {
// 0 is reserved for errors.
// Order is weird for historical reasons.
TYPE_DOUBLE = 1;
TYPE_FLOAT = 2;
// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if
// negative values are likely.
TYPE_INT64 = 3;
TYPE_UINT64 = 4;
// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if
// negative values are likely.
TYPE_INT32 = 5;
TYPE_FIXED64 = 6;
TYPE_FIXED32 = 7;
TYPE_BOOL = 8;
TYPE_STRING = 9;
// Tag-delimited aggregate.
// Group type is deprecated and not supported after google.protobuf. However, Proto3
// implementations should still be able to parse the group wire format and
// treat group fields as unknown fields. In Editions, the group wire format
// can be enabled via the `message_encoding` feature.
TYPE_GROUP = 10;
TYPE_MESSAGE = 11; // Length-delimited aggregate.
// New in version 2.
TYPE_BYTES = 12;
TYPE_UINT32 = 13;
TYPE_ENUM = 14;
TYPE_SFIXED32 = 15;
TYPE_SFIXED64 = 16;
TYPE_SINT32 = 17; // Uses ZigZag encoding.
TYPE_SINT64 = 18; // Uses ZigZag encoding.
}
enum Label {
// 0 is reserved for errors
LABEL_OPTIONAL = 1;
LABEL_REPEATED = 3;
// The required label is only allowed in google.protobuf. In proto3 and Editions
// it's explicitly prohibited. In Editions, the `field_presence` feature
// can be used to get this behavior.
LABEL_REQUIRED = 2;
}
optional string name = 1;
optional int32 number = 3;
optional Label label = 4;
// If type_name is set, this need not be set. If both this and type_name
// are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP.
optional Type type = 5;
// For message and enum types, this is the name of the type. If the name
// starts with a '.', it is fully-qualified. Otherwise, C++-like scoping
// rules are used to find the type (i.e. first the nested types within this
// message are searched, then within the parent, on up to the root
// namespace).
optional string type_name = 6;
// For extensions, this is the name of the type being extended. It is
// resolved in the same manner as type_name.
optional string extendee = 2;
// For numeric types, contains the original text representation of the value.
// For booleans, "true" or "false".
// For strings, contains the default text contents (not escaped in any way).
// For bytes, contains the C escaped value. All bytes >= 128 are escaped.
optional string default_value = 7;
// If set, gives the index of a oneof in the containing type's oneof_decl
// list. This field is a member of that oneof.
optional int32 oneof_index = 9;
// JSON name of this field. The value is set by protocol compiler. If the
// user has set a "json_name" option on this field, that option's value
// will be used. Otherwise, it's deduced from the field's name by converting
// it to camelCase.
optional string json_name = 10;
optional FieldOptions options = 8;
// If true, this is a proto3 "optional". When a proto3 field is optional, it
// tracks presence regardless of field type.
//
// When proto3_optional is true, this field must belong to a oneof to signal
// to old proto3 clients that presence is tracked for this field. This oneof
// is known as a "synthetic" oneof, and this field must be its sole member
// (each proto3 optional field gets its own synthetic oneof). Synthetic oneofs
// exist in the descriptor only, and do not generate any API. Synthetic oneofs
// must be ordered after all "real" oneofs.
//
// For message fields, proto3_optional doesn't create any semantic change,
// since non-repeated message fields always track presence. However it still
// indicates the semantic detail of whether the user wrote "optional" or not.
// This can be useful for round-tripping the .proto file. For consistency we
// give message fields a synthetic oneof also, even though it is not required
// to track presence. This is especially important because the parser can't
// tell if a field is a message or an enum, so it must always create a
// synthetic oneof.
//
// Proto2 optional fields do not set this flag, because they already indicate
// optional with `LABEL_OPTIONAL`.
optional bool proto3_optional = 17;
}
// Describes a oneof.
message OneofDescriptorProto {
optional string name = 1;
optional OneofOptions options = 2;
}
// Describes an enum type.
message EnumDescriptorProto {
optional string name = 1;
repeated EnumValueDescriptorProto value = 2;
optional EnumOptions options = 3;
// Range of reserved numeric values. Reserved values may not be used by
// entries in the same enum. Reserved ranges may not overlap.
//
// Note that this is distinct from DescriptorProto.ReservedRange in that it
// is inclusive such that it can appropriately represent the entire int32
// domain.
message EnumReservedRange {
optional int32 start = 1; // Inclusive.
optional int32 end = 2; // Inclusive.
}
// Range of reserved numeric values. Reserved numeric values may not be used
// by enum values in the same enum declaration. Reserved ranges may not
// overlap.
repeated EnumReservedRange reserved_range = 4;
// Reserved enum value names, which may not be reused. A given name may only
// be reserved once.
repeated string reserved_name = 5;
}
// Describes a value within an enum.
message EnumValueDescriptorProto {
optional string name = 1;
optional int32 number = 2;
optional EnumValueOptions options = 3;
}
// Describes a service.
message ServiceDescriptorProto {
optional string name = 1;
repeated MethodDescriptorProto method = 2;
optional ServiceOptions options = 3;
}
// Describes a method of a service.
message MethodDescriptorProto {
optional string name = 1;
// Input and output type names. These are resolved in the same way as
// FieldDescriptorProto.type_name, but must refer to a message type.
optional string input_type = 2;
optional string output_type = 3;
optional MethodOptions options = 4;
// Identifies if client streams multiple client messages
optional bool client_streaming = 5 [default = false];
// Identifies if server streams multiple server messages
optional bool server_streaming = 6 [default = false];
}
// ===================================================================
// Options
// Each of the definitions above may have "options" attached. These are
// just annotations which may cause code to be generated slightly differently
// or may contain hints for code that manipulates protocol messages.
//
// Clients may define custom options as extensions of the *Options messages.
// These extensions may not yet be known at parsing time, so the parser cannot
// store the values in them. Instead it stores them in a field in the *Options
// message called uninterpreted_option. This field must have the same name
// across all *Options messages. We then use this field to populate the
// extensions when we build a descriptor, at which point all protos have been
// parsed and so all extensions are known.
//
// Extension numbers for custom options may be chosen as follows:
// * For options which will only be used within a single application or
// organization, or for experimental options, use field numbers 50000
// through 99999. It is up to you to ensure that you do not use the
// same number for multiple options.
// * For options which will be published and used publicly by multiple
// independent entities, e-mail protobuf-global-extension-registry@google.com
// to reserve extension numbers. Simply provide your project name (e.g.
// Objective-C plugin) and your project website (if available) -- there's no
// need to explain how you intend to use them. Usually you only need one
// extension number. You can declare multiple options with only one extension
// number by putting them in a sub-message. See the Custom Options section of
// the docs for examples:
// https://developers.google.com/protocol-buffers/docs/proto#options
// If this turns out to be popular, a web service will be set up
// to automatically assign option numbers.
message FileOptions {
// Sets the Java package where classes generated from this .proto will be
// placed. By default, the proto package is used, but this is often
// inappropriate because proto packages do not normally start with backwards
// domain names.
optional string java_package = 1;
// Controls the name of the wrapper Java class generated for the .proto file.
// That class will always contain the .proto file's getDescriptor() method as
// well as any top-level extensions defined in the .proto file.
// If java_multiple_files is disabled, then all the other classes from the
// .proto file will be nested inside the single wrapper outer class.
optional string java_outer_classname = 8;
// If enabled, then the Java code generator will generate a separate .java
// file for each top-level message, enum, and service defined in the .proto
// file. Thus, these types will *not* be nested inside the wrapper class
// named by java_outer_classname. However, the wrapper class will still be
// generated to contain the file's getDescriptor() method as well as any
// top-level extensions defined in the file.
optional bool java_multiple_files = 10 [default = false];
// This option does nothing.
optional bool java_generate_equals_and_hash = 20 [deprecated=true];
// A proto2 file can set this to true to opt in to UTF-8 checking for Java,
// which will throw an exception if invalid UTF-8 is parsed from the wire or
// assigned to a string field.
//
// TODO: clarify exactly what kinds of field types this option
// applies to, and update these docs accordingly.
//
// Proto3 files already perform these checks. Setting the option explicitly to
// false has no effect: it cannot be used to opt proto3 files out of UTF-8
// checks.
optional bool java_string_check_utf8 = 27 [default = false];
// Generated classes can be optimized for speed or code size.
enum OptimizeMode {
SPEED = 1; // Generate complete code for parsing, serialization,
// etc.
CODE_SIZE = 2; // Use ReflectionOps to implement these methods.
LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime.
}
optional OptimizeMode optimize_for = 9 [default = SPEED];
// Sets the Go package where structs generated from this .proto will be
// placed. If omitted, the Go package will be derived from the following:
// - The basename of the package import path, if provided.
// - Otherwise, the package statement in the .proto file, if present.
// - Otherwise, the basename of the .proto file, without extension.
optional string go_package = 11;
// Should generic services be generated in each language? "Generic" services
// are not specific to any particular RPC system. They are generated by the
// main code generators in each language (without additional plugins).
// Generic services were the only kind of service generation supported by
// early versions of google.protobuf.
//
// Generic services are now considered deprecated in favor of using plugins
// that generate code specific to your particular RPC system. Therefore,
// these default to false. Old code which depends on generic services should
// explicitly set them to true.
optional bool cc_generic_services = 16 [default = false];
optional bool java_generic_services = 17 [default = false];
optional bool py_generic_services = 18 [default = false];
reserved 42; // removed php_generic_services
reserved "php_generic_services";
// Is this file deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for everything in the file, or it will be completely ignored; in the very
// least, this is a formalization for deprecating files.
optional bool deprecated = 23 [default = false];
// Enables the use of arenas for the proto messages in this file. This applies
// only to generated classes for C++.
optional bool cc_enable_arenas = 31 [default = true];
// Sets the objective c class prefix which is prepended to all objective c
// generated classes from this .proto. There is no default.
optional string objc_class_prefix = 36;
// Namespace for generated classes; defaults to the package.
optional string csharp_namespace = 37;
// By default Swift generators will take the proto package and CamelCase it
// replacing '.' with underscore and use that to prefix the types/symbols
// defined. When this options is provided, they will use this value instead
// to prefix the types/symbols defined.
optional string swift_prefix = 39;
// Sets the php class prefix which is prepended to all php generated classes
// from this .proto. Default is empty.
optional string php_class_prefix = 40;
// Use this option to change the namespace of php generated classes. Default
// is empty. When this option is empty, the package name will be used for
// determining the namespace.
optional string php_namespace = 41;
// Use this option to change the namespace of php generated metadata classes.
// Default is empty. When this option is empty, the proto file name will be
// used for determining the namespace.
optional string php_metadata_namespace = 44;
// Use this option to change the package of ruby generated classes. Default
// is empty. When this option is not set, the package name will be used for
// determining the ruby package.
optional string ruby_package = 45;
// Any features defined in the specific edition.
optional FeatureSet features = 50;
// The parser stores options it doesn't recognize here.
// See the documentation for the "Options" section above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message.
// See the documentation for the "Options" section above.
extensions 1000 to max;
reserved 38;
}
message MessageOptions {
// Set true to use the old proto1 MessageSet wire format for extensions.
// This is provided for backwards-compatibility with the MessageSet wire
// format. You should not use this for any other reason: It's less
// efficient, has fewer features, and is more complicated.
//
// The message must be defined exactly as follows:
// message Foo {
// option message_set_wire_format = true;
// extensions 4 to max;
// }
// Note that the message cannot have any defined fields; MessageSets only
// have extensions.
//
// All extensions of your type must be singular messages; e.g. they cannot
// be int32s, enums, or repeated messages.
//
// Because this is an option, the above two restrictions are not enforced by
// the protocol compiler.
optional bool message_set_wire_format = 1 [default = false];
// Disables the generation of the standard "descriptor()" accessor, which can
// conflict with a field of the same name. This is meant to make migration
// from proto1 easier; new code should avoid fields named "descriptor".
optional bool no_standard_descriptor_accessor = 2 [default = false];
// Is this message deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the message, or it will be completely ignored; in the very least,
// this is a formalization for deprecating messages.
optional bool deprecated = 3 [default = false];
reserved 4, 5, 6;
// Whether the message is an automatically generated map entry type for the
// maps field.
//
// For maps fields:
// map<KeyType, ValueType> map_field = 1;
// The parsed descriptor looks like:
// message MapFieldEntry {
// option map_entry = true;
// optional KeyType key = 1;
// optional ValueType value = 2;
// }
// repeated MapFieldEntry map_field = 1;
//
// Implementations may choose not to generate the map_entry=true message, but
// use a native map in the target language to hold the keys and values.
// The reflection APIs in such implementations still need to work as
// if the field is a repeated message field.
//
// NOTE: Do not set the option in .proto files. Always use the maps syntax
// instead. The option should only be implicitly set by the proto compiler
// parser.
optional bool map_entry = 7;
reserved 8; // javalite_serializable
reserved 9; // javanano_as_lite
// Enable the legacy handling of JSON field name conflicts. This lowercases
// and strips underscored from the fields before comparison in proto3 only.
// The new behavior takes `json_name` into account and applies to proto2 as
// well.
//
// This should only be used as a temporary measure against broken builds due
// to the change in behavior for JSON field name conflicts.
//
// TODO This is legacy behavior we plan to remove once downstream
// teams have had time to migrate.
optional bool deprecated_legacy_json_field_conflicts = 11 [deprecated = true];
// Any features defined in the specific edition.
optional FeatureSet features = 12;
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message FieldOptions {
// NOTE: ctype is deprecated. Use `features.(pb.cpp).string_type` instead.
// The ctype option instructs the C++ code generator to use a different
// representation of the field than it normally would. See the specific
// options below. This option is only implemented to support use of
// [ctype=CORD] and [ctype=STRING] (the default) on non-repeated fields of
// type "bytes" in the open source release.
// TODO: make ctype actually deprecated.
optional CType ctype = 1 [/*deprecated = true,*/ default = STRING];
enum CType {
// Default mode.
STRING = 0;
// The option [ctype=CORD] may be applied to a non-repeated field of type
// "bytes". It indicates that in C++, the data should be stored in a Cord
// instead of a string. For very large strings, this may reduce memory
// fragmentation. It may also allow better performance when parsing from a
// Cord, or when parsing with aliasing enabled, as the parsed Cord may then
// alias the original buffer.
CORD = 1;
STRING_PIECE = 2;
}
// The packed option can be enabled for repeated primitive fields to enable
// a more efficient representation on the wire. Rather than repeatedly
// writing the tag and type for each element, the entire array is encoded as
// a single length-delimited blob. In proto3, only explicit setting it to
// false will avoid using packed encoding. This option is prohibited in
// Editions, but the `repeated_field_encoding` feature can be used to control
// the behavior.
optional bool packed = 2;
// The jstype option determines the JavaScript type used for values of the
// field. The option is permitted only for 64 bit integral and fixed types
// (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING
// is represented as JavaScript string, which avoids loss of precision that
// can happen when a large value is converted to a floating point JavaScript.
// Specifying JS_NUMBER for the jstype causes the generated JavaScript code to
// use the JavaScript "number" type. The behavior of the default option
// JS_NORMAL is implementation dependent.
//
// This option is an enum to permit additional types to be added, e.g.
// goog.math.Integer.
optional JSType jstype = 6 [default = JS_NORMAL];
enum JSType {
// Use the default type.
JS_NORMAL = 0;
// Use JavaScript strings.
JS_STRING = 1;
// Use JavaScript numbers.
JS_NUMBER = 2;
}
// Should this field be parsed lazily? Lazy applies only to message-type
// fields. It means that when the outer message is initially parsed, the
// inner message's contents will not be parsed but instead stored in encoded
// form. The inner message will actually be parsed when it is first accessed.
//
// This is only a hint. Implementations are free to choose whether to use
// eager or lazy parsing regardless of the value of this option. However,
// setting this option true suggests that the protocol author believes that
// using lazy parsing on this field is worth the additional bookkeeping
// overhead typically needed to implement it.
//
// This option does not affect the public interface of any generated code;
// all method signatures remain the same. Furthermore, thread-safety of the
// interface is not affected by this option; const methods remain safe to
// call from multiple threads concurrently, while non-const methods continue
// to require exclusive access.
//
// Note that lazy message fields are still eagerly verified to check
// ill-formed wireformat or missing required fields. Calling IsInitialized()
// on the outer message would fail if the inner message has missing required
// fields. Failed verification would result in parsing failure (except when
// uninitialized messages are acceptable).
optional bool lazy = 5 [default = false];
// unverified_lazy does no correctness checks on the byte stream. This should
// only be used where lazy with verification is prohibitive for performance
// reasons.
optional bool unverified_lazy = 15 [default = false];
// Is this field deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for accessors, or it will be completely ignored; in the very least, this
// is a formalization for deprecating fields.
optional bool deprecated = 3 [default = false];
// For Google-internal migration only. Do not use.
optional bool weak = 10 [default = false];
// Indicate that the field value should not be printed out when using debug
// formats, e.g. when the field contains sensitive credentials.
optional bool debug_redact = 16 [default = false];
// If set to RETENTION_SOURCE, the option will be omitted from the binary.
// Note: as of January 2023, support for this is in progress and does not yet
// have an effect (b/264593489).
enum OptionRetention {
RETENTION_UNKNOWN = 0;
RETENTION_RUNTIME = 1;
RETENTION_SOURCE = 2;
}
optional OptionRetention retention = 17;
// This indicates the types of entities that the field may apply to when used
// as an option. If it is unset, then the field may be freely used as an
// option on any kind of entity. Note: as of January 2023, support for this is
// in progress and does not yet have an effect (b/264593489).
enum OptionTargetType {
TARGET_TYPE_UNKNOWN = 0;
TARGET_TYPE_FILE = 1;
TARGET_TYPE_EXTENSION_RANGE = 2;
TARGET_TYPE_MESSAGE = 3;
TARGET_TYPE_FIELD = 4;
TARGET_TYPE_ONEOF = 5;
TARGET_TYPE_ENUM = 6;
TARGET_TYPE_ENUM_ENTRY = 7;
TARGET_TYPE_SERVICE = 8;
TARGET_TYPE_METHOD = 9;
}
repeated OptionTargetType targets = 19;
message EditionDefault {
optional Edition edition = 3;
optional string value = 2; // Textproto value.
}
repeated EditionDefault edition_defaults = 20;
// Any features defined in the specific edition.
optional FeatureSet features = 21;
// Information about the support window of a feature.
message FeatureSupport {
// The edition that this feature was first available in. In editions
// earlier than this one, the default assigned to EDITION_LEGACY will be
// used, and proto files will not be able to override it.
optional Edition edition_introduced = 1;
// The edition this feature becomes deprecated in. Using this after this
// edition may trigger warnings.
optional Edition edition_deprecated = 2;
// The deprecation warning text if this feature is used after the edition it
// was marked deprecated in.
optional string deprecation_warning = 3;
// The edition this feature is no longer available in. In editions after
// this one, the last default assigned will be used, and proto files will
// not be able to override it.
optional Edition edition_removed = 4;
}
optional FeatureSupport feature_support = 22;
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
reserved 4; // removed jtype
reserved 18; // reserve target, target_obsolete_do_not_use
}
message OneofOptions {
// Any features defined in the specific edition.
optional FeatureSet features = 1;
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message EnumOptions {
// Set this option to true to allow mapping different tag names to the same
// value.
optional bool allow_alias = 2;
// Is this enum deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the enum, or it will be completely ignored; in the very least, this
// is a formalization for deprecating enums.
optional bool deprecated = 3 [default = false];
reserved 5; // javanano_as_lite
// Enable the legacy handling of JSON field name conflicts. This lowercases
// and strips underscored from the fields before comparison in proto3 only.
// The new behavior takes `json_name` into account and applies to proto2 as
// well.
// TODO Remove this legacy behavior once downstream teams have
// had time to migrate.
optional bool deprecated_legacy_json_field_conflicts = 6 [deprecated = true];
// Any features defined in the specific edition.
optional FeatureSet features = 7;
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message EnumValueOptions {
// Is this enum value deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the enum value, or it will be completely ignored; in the very least,
// this is a formalization for deprecating enum values.
optional bool deprecated = 1 [default = false];
// Any features defined in the specific edition.
optional FeatureSet features = 2;
// Indicate that fields annotated with this enum value should not be printed
// out when using debug formats, e.g. when the field contains sensitive
// credentials.
optional bool debug_redact = 3 [default = false];
// Information about the support window of a feature value.
optional FieldOptions.FeatureSupport feature_support = 4;
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message ServiceOptions {
// Any features defined in the specific edition.
optional FeatureSet features = 34;
// Note: Field numbers 1 through 32 are reserved for Google's internal RPC
// framework. We apologize for hoarding these numbers to ourselves, but
// we were already using them long before we decided to release Protocol
// Buffers.
// Is this service deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the service, or it will be completely ignored; in the very least,
// this is a formalization for deprecating services.
optional bool deprecated = 33 [default = false];
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message MethodOptions {
// Note: Field numbers 1 through 32 are reserved for Google's internal RPC
// framework. We apologize for hoarding these numbers to ourselves, but
// we were already using them long before we decided to release Protocol
// Buffers.
// Is this method deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the method, or it will be completely ignored; in the very least,
// this is a formalization for deprecating methods.
optional bool deprecated = 33 [default = false];
// Is this method side-effect-free (or safe in HTTP parlance), or idempotent,
// or neither? HTTP based RPC implementation may choose GET verb for safe
// methods, and PUT verb for idempotent methods instead of the default POST.
enum IdempotencyLevel {
IDEMPOTENCY_UNKNOWN = 0;
NO_SIDE_EFFECTS = 1; // implies idempotent
IDEMPOTENT = 2; // idempotent, but may have side effects
}
optional IdempotencyLevel idempotency_level = 34
[default = IDEMPOTENCY_UNKNOWN];
// Any features defined in the specific edition.
optional FeatureSet features = 35;
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
// A message representing a option the parser does not recognize. This only
// appears in options protos created by the compiler::Parser class.
// DescriptorPool resolves these when building Descriptor objects. Therefore,
// options protos in descriptor objects (e.g. returned by Descriptor::options(),
// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions
// in them.
message UninterpretedOption {
// The name of the uninterpreted option. Each string represents a segment in
// a dot-separated name. is_extension is true iff a segment represents an
// extension (denoted with parentheses in options specs in .proto files).
// E.g.,{ ["foo", false], ["bar.baz", true], ["moo", false] } represents
// "foo.(bar.baz).moo".
message NamePart {
required string name_part = 1;
required bool is_extension = 2;
}
repeated NamePart name = 2;
// The value of the uninterpreted option, in whatever type the tokenizer
// identified it as during parsing. Exactly one of these should be set.
optional string identifier_value = 3;
optional uint64 positive_int_value = 4;
optional int64 negative_int_value = 5;
optional double double_value = 6;
optional bytes string_value = 7;
optional string aggregate_value = 8;
}
// ===================================================================
// Features
// TODO Enums in C++ gencode (and potentially other languages) are
// not well scoped. This means that each of the feature enums below can clash
// with each other. The short names we've chosen maximize call-site
// readability, but leave us very open to this scenario. A future feature will
// be designed and implemented to handle this, hopefully before we ever hit a
// conflict here.
message FeatureSet {
enum FieldPresence {
FIELD_PRESENCE_UNKNOWN = 0;
EXPLICIT = 1;
IMPLICIT = 2;
LEGACY_REQUIRED = 3;
}
optional FieldPresence field_presence = 1 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_FIELD,
targets = TARGET_TYPE_FILE,
feature_support = {
edition_introduced: EDITION_2023,
},
edition_defaults = { edition: EDITION_LEGACY, value: "EXPLICIT" },
edition_defaults = { edition: EDITION_PROTO3, value: "IMPLICIT" },
edition_defaults = { edition: EDITION_2023, value: "EXPLICIT" }
];
enum EnumType {
ENUM_TYPE_UNKNOWN = 0;
OPEN = 1;
CLOSED = 2;
}
optional EnumType enum_type = 2 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_ENUM,
targets = TARGET_TYPE_FILE,
feature_support = {
edition_introduced: EDITION_2023,
},
edition_defaults = { edition: EDITION_LEGACY, value: "CLOSED" },
edition_defaults = { edition: EDITION_PROTO3, value: "OPEN" }
];
enum RepeatedFieldEncoding {
REPEATED_FIELD_ENCODING_UNKNOWN = 0;
PACKED = 1;
EXPANDED = 2;
}
optional RepeatedFieldEncoding repeated_field_encoding = 3 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_FIELD,
targets = TARGET_TYPE_FILE,
feature_support = {
edition_introduced: EDITION_2023,
},
edition_defaults = { edition: EDITION_LEGACY, value: "EXPANDED" },
edition_defaults = { edition: EDITION_PROTO3, value: "PACKED" }
];
enum Utf8Validation {
UTF8_VALIDATION_UNKNOWN = 0;
VERIFY = 2;
NONE = 3;
reserved 1;
}
optional Utf8Validation utf8_validation = 4 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_FIELD,
targets = TARGET_TYPE_FILE,
feature_support = {
edition_introduced: EDITION_2023,
},
edition_defaults = { edition: EDITION_LEGACY, value: "NONE" },
edition_defaults = { edition: EDITION_PROTO3, value: "VERIFY" }
];
enum MessageEncoding {
MESSAGE_ENCODING_UNKNOWN = 0;
LENGTH_PREFIXED = 1;
DELIMITED = 2;
}
optional MessageEncoding message_encoding = 5 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_FIELD,
targets = TARGET_TYPE_FILE,
feature_support = {
edition_introduced: EDITION_2023,
},
edition_defaults = { edition: EDITION_LEGACY, value: "LENGTH_PREFIXED" }
];
enum JsonFormat {
JSON_FORMAT_UNKNOWN = 0;
ALLOW = 1;
LEGACY_BEST_EFFORT = 2;
}
optional JsonFormat json_format = 6 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_MESSAGE,
targets = TARGET_TYPE_ENUM,
targets = TARGET_TYPE_FILE,
feature_support = {
edition_introduced: EDITION_2023,
},
edition_defaults = { edition: EDITION_LEGACY, value: "LEGACY_BEST_EFFORT" },
edition_defaults = { edition: EDITION_PROTO3, value: "ALLOW" }
];
reserved 999;
extensions 1000 to 9994 [
declaration = {
number: 1000,
full_name: ".pb.cpp",
type: ".pb.CppFeatures"
},
declaration = {
number: 1001,
full_name: ".pb.java",
type: ".pb.JavaFeatures"
},
declaration = { number: 1002, full_name: ".pb.go", type: ".pb.GoFeatures" },
declaration = {
number: 9990,
full_name: ".pb.proto1",
type: ".pb.Proto1Features"
}
];
extensions 9995 to 9999; // For internal testing
extensions 10000; // for https://github.com/bufbuild/protobuf-es
}
// A compiled specification for the defaults of a set of features. These
// messages are generated from FeatureSet extensions and can be used to seed
// feature resolution. The resolution with this object becomes a simple search
// for the closest matching edition, followed by proto merges.
message FeatureSetDefaults {
// A map from every known edition with a unique set of defaults to its
// defaults. Not all editions may be contained here. For a given edition,
// the defaults at the closest matching edition ordered at or before it should
// be used. This field must be in strict ascending order by edition.
message FeatureSetEditionDefault {
optional Edition edition = 3;
// Defaults of features that can be overridden in this edition.
optional FeatureSet overridable_features = 4;
// Defaults of features that can't be overridden in this edition.
optional FeatureSet fixed_features = 5;
reserved 1, 2;
reserved "features";
}
repeated FeatureSetEditionDefault defaults = 1;
// The minimum supported edition (inclusive) when this was constructed.
// Editions before this will not have defaults.
optional Edition minimum_edition = 4;
// The maximum known edition (inclusive) when this was constructed. Editions
// after this will not have reliable defaults.
optional Edition maximum_edition = 5;
}
// ===================================================================
// Optional source code info
// Encapsulates information about the original source file from which a
// FileDescriptorProto was generated.
message SourceCodeInfo {
// A Location identifies a piece of source code in a .proto file which
// corresponds to a particular definition. This information is intended
// to be useful to IDEs, code indexers, documentation generators, and similar
// tools.
//
// For example, say we have a file like:
// message Foo {
// optional string foo = 1;
// }
// Let's look at just the field definition:
// optional string foo = 1;
// ^ ^^ ^^ ^ ^^^
// a bc de f ghi
// We have the following locations:
// span path represents
// [a,i) [ 4, 0, 2, 0 ] The whole field definition.
// [a,b) [ 4, 0, 2, 0, 4 ] The label (optional).
// [c,d) [ 4, 0, 2, 0, 5 ] The type (string).
// [e,f) [ 4, 0, 2, 0, 1 ] The name (foo).
// [g,h) [ 4, 0, 2, 0, 3 ] The number (1).
//
// Notes:
// - A location may refer to a repeated field itself (i.e. not to any
// particular index within it). This is used whenever a set of elements are
// logically enclosed in a single code segment. For example, an entire
// extend block (possibly containing multiple extension definitions) will
// have an outer location whose path refers to the "extensions" repeated
// field without an index.
// - Multiple locations may have the same path. This happens when a single
// logical declaration is spread out across multiple places. The most
// obvious example is the "extend" block again -- there may be multiple
// extend blocks in the same scope, each of which will have the same path.
// - A location's span is not always a subset of its parent's span. For
// example, the "extendee" of an extension declaration appears at the
// beginning of the "extend" block and is shared by all extensions within
// the block.
// - Just because a location's span is a subset of some other location's span
// does not mean that it is a descendant. For example, a "group" defines
// both a type and a field in a single declaration. Thus, the locations
// corresponding to the type and field and their components will overlap.
// - Code which tries to interpret locations should probably be designed to
// ignore those that it doesn't understand, as more types of locations could
// be recorded in the future.
repeated Location location = 1;
message Location {
// Identifies which part of the FileDescriptorProto was defined at this
// location.
//
// Each element is a field number or an index. They form a path from
// the root FileDescriptorProto to the place where the definition appears.
// For example, this path:
// [ 4, 3, 2, 7, 1 ]
// refers to:
// file.message_type(3) // 4, 3
// .field(7) // 2, 7
// .name() // 1
// This is because FileDescriptorProto.message_type has field number 4:
// repeated DescriptorProto message_type = 4;
// and DescriptorProto.field has field number 2:
// repeated FieldDescriptorProto field = 2;
// and FieldDescriptorProto.name has field number 1:
// optional string name = 1;
//
// Thus, the above path gives the location of a field name. If we removed
// the last element:
// [ 4, 3, 2, 7 ]
// this path refers to the whole field declaration (from the beginning
// of the label to the terminating semicolon).
repeated int32 path = 1 [packed = true];
// Always has exactly three or four elements: start line, start column,
// end line (optional, otherwise assumed same as start line), end column.
// These are packed into a single field for efficiency. Note that line
// and column numbers are zero-based -- typically you will want to add
// 1 to each before displaying to a user.
repeated int32 span = 2 [packed = true];
// If this SourceCodeInfo represents a complete declaration, these are any
// comments appearing before and after the declaration which appear to be
// attached to the declaration.
//
// A series of line comments appearing on consecutive lines, with no other
// tokens appearing on those lines, will be treated as a single comment.
//
// leading_detached_comments will keep paragraphs of comments that appear
// before (but not connected to) the current element. Each paragraph,
// separated by empty lines, will be one comment element in the repeated
// field.
//
// Only the comment content is provided; comment markers (e.g. //) are
// stripped out. For block comments, leading whitespace and an asterisk
// will be stripped from the beginning of each line other than the first.
// Newlines are included in the output.
//
// Examples:
//
// optional int32 foo = 1; // Comment attached to foo.
// // Comment attached to bar.
// optional int32 bar = 2;
//
// optional string baz = 3;
// // Comment attached to baz.
// // Another line attached to baz.
//
// // Comment attached to moo.
// //
// // Another line attached to moo.
// optional double moo = 4;
//
// // Detached comment for corge. This is not leading or trailing comments
// // to moo or corge because there are blank lines separating it from
// // both.
//
// // Detached comment for corge paragraph 2.
//
// optional string corge = 5;
// /* Block comment attached
// * to corge. Leading asterisks
// * will be removed. */
// /* Block comment attached to
// * grault. */
// optional int32 grault = 6;
//
// // ignored detached comments.
optional string leading_comments = 3;
optional string trailing_comments = 4;
repeated string leading_detached_comments = 6;
}
}
// Describes the relationship between generated code and its original source
// file. A GeneratedCodeInfo message is associated with only one generated
// source file, but may contain references to different source .proto files.
message GeneratedCodeInfo {
// An Annotation connects some span of text in generated code to an element
// of its generating .proto file.
repeated Annotation annotation = 1;
message Annotation {
// Identifies the element in the original source .proto file. This field
// is formatted the same as SourceCodeInfo.Location.path.
repeated int32 path = 1 [packed = true];
// Identifies the filesystem path to the original source .proto.
optional string source_file = 2;
// Identifies the starting offset in bytes in the generated code
// that relates to the identified object.
optional int32 begin = 3;
// Identifies the ending offset in bytes in the generated code that
// relates to the identified object. The end offset should be one past
// the last relevant byte (so the length of the text = end - begin).
optional int32 end = 4;
// Represents the identified object's effect on the element in the original
// .proto file.
enum Semantic {
// There is no effect or the effect is indescribable.
NONE = 0;
// The element is set or otherwise mutated.
SET = 1;
// An alias to the element is returned.
ALIAS = 2;
}
optional Semantic semantic = 5;
}
}
// 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
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.10
// protoc v6.33.0
// source: weather.proto
package weather_system
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type AlertMessage_AlertType int32
const (
AlertMessage_UNKNOWN AlertMessage_AlertType = 0
AlertMessage_HIGH_TEMPERATURE AlertMessage_AlertType = 1
AlertMessage_LOW_PRESSURE AlertMessage_AlertType = 2
AlertMessage_HIGH_HUMIDITY AlertMessage_AlertType = 3
AlertMessage_NORMAL AlertMessage_AlertType = 4
)
// Enum value maps for AlertMessage_AlertType.
var (
AlertMessage_AlertType_name = map[int32]string{
0: "UNKNOWN",
1: "HIGH_TEMPERATURE",
2: "LOW_PRESSURE",
3: "HIGH_HUMIDITY",
4: "NORMAL",
}
AlertMessage_AlertType_value = map[string]int32{
"UNKNOWN": 0,
"HIGH_TEMPERATURE": 1,
"LOW_PRESSURE": 2,
"HIGH_HUMIDITY": 3,
"NORMAL": 4,
}
)
func (x AlertMessage_AlertType) Enum() *AlertMessage_AlertType {
p := new(AlertMessage_AlertType)
*p = x
return p
}
func (x AlertMessage_AlertType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (AlertMessage_AlertType) Descriptor() protoreflect.EnumDescriptor {
return file_weather_proto_enumTypes[0].Descriptor()
}
func (AlertMessage_AlertType) Type() protoreflect.EnumType {
return &file_weather_proto_enumTypes[0]
}
func (x AlertMessage_AlertType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use AlertMessage_AlertType.Descriptor instead.
func (AlertMessage_AlertType) EnumDescriptor() ([]byte, []int) {
return file_weather_proto_rawDescGZIP(), []int{2, 0}
}
// 1. Weather Data sent from the sensor (WeatherData)
type WeatherData struct {
state protoimpl.MessageState `protogen:"open.v1"`
TemperatureCelsius float64 `protobuf:"fixed64,1,opt,name=temperature_celsius,json=temperatureCelsius,proto3" json:"temperature_celsius,omitempty"` // Temperature in Celsius (e.g., 25.5)
HumidityPercent float64 `protobuf:"fixed64,2,opt,name=humidity_percent,json=humidityPercent,proto3" json:"humidity_percent,omitempty"` // Humidity percentage (e.g., 60.0)
PressureHpa float64 `protobuf:"fixed64,3,opt,name=pressure_hpa,json=pressureHpa,proto3" json:"pressure_hpa,omitempty"` // Pressure in Hectopascals (e.g., 1012.3)
Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Unix timestamp of the reading
StationId string `protobuf:"bytes,5,opt,name=station_id,json=stationId,proto3" json:"station_id,omitempty"` // Identifier of the weather station
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *WeatherData) Reset() {
*x = WeatherData{}
mi := &file_weather_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *WeatherData) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WeatherData) ProtoMessage() {}
func (x *WeatherData) ProtoReflect() protoreflect.Message {
mi := &file_weather_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use WeatherData.ProtoReflect.Descriptor instead.
func (*WeatherData) Descriptor() ([]byte, []int) {
return file_weather_proto_rawDescGZIP(), []int{0}
}
func (x *WeatherData) GetTemperatureCelsius() float64 {
if x != nil {
return x.TemperatureCelsius
}
return 0
}
func (x *WeatherData) GetHumidityPercent() float64 {
if x != nil {
return x.HumidityPercent
}
return 0
}
func (x *WeatherData) GetPressureHpa() float64 {
if x != nil {
return x.PressureHpa
}
return 0
}
func (x *WeatherData) GetTimestamp() int64 {
if x != nil {
return x.Timestamp
}
return 0
}
func (x *WeatherData) GetStationId() string {
if x != nil {
return x.StationId
}
return ""
}
// 2. Calculated Weather Statistics (WeatherStats)
type WeatherStats struct {
state protoimpl.MessageState `protogen:"open.v1"`
AvgTemperature float64 `protobuf:"fixed64,1,opt,name=avg_temperature,json=avgTemperature,proto3" json:"avg_temperature,omitempty"`
AvgHumidity float64 `protobuf:"fixed64,2,opt,name=avg_humidity,json=avgHumidity,proto3" json:"avg_humidity,omitempty"`
AvgPressure float64 `protobuf:"fixed64,3,opt,name=avg_pressure,json=avgPressure,proto3" json:"avg_pressure,omitempty"`
ReportTime int64 `protobuf:"varint,4,opt,name=report_time,json=reportTime,proto3" json:"report_time,omitempty"`
// Optional field to send alerts within the data stream
CurrentAlert *AlertMessage `protobuf:"bytes,5,opt,name=current_alert,json=currentAlert,proto3" json:"current_alert,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *WeatherStats) Reset() {
*x = WeatherStats{}
mi := &file_weather_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *WeatherStats) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WeatherStats) ProtoMessage() {}
func (x *WeatherStats) ProtoReflect() protoreflect.Message {
mi := &file_weather_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use WeatherStats.ProtoReflect.Descriptor instead.
func (*WeatherStats) Descriptor() ([]byte, []int) {
return file_weather_proto_rawDescGZIP(), []int{1}
}
func (x *WeatherStats) GetAvgTemperature() float64 {
if x != nil {
return x.AvgTemperature
}
return 0
}
func (x *WeatherStats) GetAvgHumidity() float64 {
if x != nil {
return x.AvgHumidity
}
return 0
}
func (x *WeatherStats) GetAvgPressure() float64 {
if x != nil {
return x.AvgPressure
}
return 0
}
func (x *WeatherStats) GetReportTime() int64 {
if x != nil {
return x.ReportTime
}
return 0
}
func (x *WeatherStats) GetCurrentAlert() *AlertMessage {
if x != nil {
return x.CurrentAlert
}
return nil
}
// 3. Alert Message (AlertMessage)
type AlertMessage struct {
state protoimpl.MessageState `protogen:"open.v1"`
Type AlertMessage_AlertType `protobuf:"varint,1,opt,name=type,proto3,enum=weather_system.AlertMessage_AlertType" json:"type,omitempty"`
Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
Value float64 `protobuf:"fixed64,3,opt,name=value,proto3" json:"value,omitempty"`
Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AlertMessage) Reset() {
*x = AlertMessage{}
mi := &file_weather_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AlertMessage) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AlertMessage) ProtoMessage() {}
func (x *AlertMessage) ProtoReflect() protoreflect.Message {
mi := &file_weather_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AlertMessage.ProtoReflect.Descriptor instead.
func (*AlertMessage) Descriptor() ([]byte, []int) {
return file_weather_proto_rawDescGZIP(), []int{2}
}
func (x *AlertMessage) GetType() AlertMessage_AlertType {
if x != nil {
return x.Type
}
return AlertMessage_UNKNOWN
}
func (x *AlertMessage) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
func (x *AlertMessage) GetValue() float64 {
if x != nil {
return x.Value
}
return 0
}
func (x *AlertMessage) GetTimestamp() int64 {
if x != nil {
return x.Timestamp
}
return 0
}
// 4. Authentication Metadata (AuthMetadata)
// *Note: Token is typically sent via gRPC Metadata, but defined here for completeness*
type AuthMetadata struct {
state protoimpl.MessageState `protogen:"open.v1"`
ApiToken string `protobuf:"bytes,1,opt,name=api_token,json=apiToken,proto3" json:"api_token,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AuthMetadata) Reset() {
*x = AuthMetadata{}
mi := &file_weather_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AuthMetadata) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AuthMetadata) ProtoMessage() {}
func (x *AuthMetadata) ProtoReflect() protoreflect.Message {
mi := &file_weather_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AuthMetadata.ProtoReflect.Descriptor instead.
func (*AuthMetadata) Descriptor() ([]byte, []int) {
return file_weather_proto_rawDescGZIP(), []int{3}
}
func (x *AuthMetadata) GetApiToken() string {
if x != nil {
return x.ApiToken
}
return ""
}
// Empty message
type Empty struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Empty) Reset() {
*x = Empty{}
mi := &file_weather_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Empty) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Empty) ProtoMessage() {}
func (x *Empty) ProtoReflect() protoreflect.Message {
mi := &file_weather_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Empty.ProtoReflect.Descriptor instead.
func (*Empty) Descriptor() ([]byte, []int) {
return file_weather_proto_rawDescGZIP(), []int{4}
}
var File_weather_proto protoreflect.FileDescriptor
const file_weather_proto_rawDesc = "" +
"\n" +
"\rweather.proto\x12\x0eweather_system\"\xc9\x01\n" +
"\vWeatherData\x12/\n" +
"\x13temperature_celsius\x18\x01 \x01(\x01R\x12temperatureCelsius\x12)\n" +
"\x10humidity_percent\x18\x02 \x01(\x01R\x0fhumidityPercent\x12!\n" +
"\fpressure_hpa\x18\x03 \x01(\x01R\vpressureHpa\x12\x1c\n" +
"\ttimestamp\x18\x04 \x01(\x03R\ttimestamp\x12\x1d\n" +
"\n" +
"station_id\x18\x05 \x01(\tR\tstationId\"\xe1\x01\n" +
"\fWeatherStats\x12'\n" +
"\x0favg_temperature\x18\x01 \x01(\x01R\x0eavgTemperature\x12!\n" +
"\favg_humidity\x18\x02 \x01(\x01R\vavgHumidity\x12!\n" +
"\favg_pressure\x18\x03 \x01(\x01R\vavgPressure\x12\x1f\n" +
"\vreport_time\x18\x04 \x01(\x03R\n" +
"reportTime\x12A\n" +
"\rcurrent_alert\x18\x05 \x01(\v2\x1c.weather_system.AlertMessageR\fcurrentAlert\"\xf9\x01\n" +
"\fAlertMessage\x12:\n" +
"\x04type\x18\x01 \x01(\x0e2&.weather_system.AlertMessage.AlertTypeR\x04type\x12\x18\n" +
"\amessage\x18\x02 \x01(\tR\amessage\x12\x14\n" +
"\x05value\x18\x03 \x01(\x01R\x05value\x12\x1c\n" +
"\ttimestamp\x18\x04 \x01(\x03R\ttimestamp\"_\n" +
"\tAlertType\x12\v\n" +
"\aUNKNOWN\x10\x00\x12\x14\n" +
"\x10HIGH_TEMPERATURE\x10\x01\x12\x10\n" +
"\fLOW_PRESSURE\x10\x02\x12\x11\n" +
"\rHIGH_HUMIDITY\x10\x03\x12\n" +
"\n" +
"\x06NORMAL\x10\x04\"+\n" +
"\fAuthMetadata\x12\x1b\n" +
"\tapi_token\x18\x01 \x01(\tR\bapiToken\"\a\n" +
"\x05Empty2\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\x12D\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(\x010\x01B\x1aZ\x18sensor_go/weather_systemb\x06proto3"
var (
file_weather_proto_rawDescOnce sync.Once
file_weather_proto_rawDescData []byte
)
func file_weather_proto_rawDescGZIP() []byte {
file_weather_proto_rawDescOnce.Do(func() {
file_weather_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_weather_proto_rawDesc), len(file_weather_proto_rawDesc)))
})
return file_weather_proto_rawDescData
}
var file_weather_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_weather_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_weather_proto_goTypes = []any{
(AlertMessage_AlertType)(0), // 0: weather_system.AlertMessage.AlertType
(*WeatherData)(nil), // 1: weather_system.WeatherData
(*WeatherStats)(nil), // 2: weather_system.WeatherStats
(*AlertMessage)(nil), // 3: weather_system.AlertMessage
(*AuthMetadata)(nil), // 4: weather_system.AuthMetadata
(*Empty)(nil), // 5: weather_system.Empty
}
var file_weather_proto_depIdxs = []int32{
3, // 0: weather_system.WeatherStats.current_alert:type_name -> weather_system.AlertMessage
0, // 1: weather_system.AlertMessage.type:type_name -> weather_system.AlertMessage.AlertType
1, // 2: weather_system.WeatherAnalyticsService.StreamWeatherData:input_type -> weather_system.WeatherData
5, // 3: weather_system.WeatherAnalyticsService.StreamAnalytics:input_type -> weather_system.Empty
5, // 4: weather_system.WeatherAnalyticsService.GetLastReport:input_type -> weather_system.Empty
3, // 5: weather_system.WeatherAnalyticsService.ControlSensor:input_type -> weather_system.AlertMessage
2, // 6: weather_system.WeatherAnalyticsService.StreamWeatherData:output_type -> weather_system.WeatherStats
2, // 7: weather_system.WeatherAnalyticsService.StreamAnalytics:output_type -> weather_system.WeatherStats
2, // 8: weather_system.WeatherAnalyticsService.GetLastReport:output_type -> weather_system.WeatherStats
1, // 9: weather_system.WeatherAnalyticsService.ControlSensor:output_type -> weather_system.WeatherData
6, // [6:10] is the sub-list for method output_type
2, // [2:6] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_weather_proto_init() }
func file_weather_proto_init() {
if File_weather_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_weather_proto_rawDesc), len(file_weather_proto_rawDesc)),
NumEnums: 1,
NumMessages: 5,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_weather_proto_goTypes,
DependencyIndexes: file_weather_proto_depIdxs,
EnumInfos: file_weather_proto_enumTypes,
MessageInfos: file_weather_proto_msgTypes,
}.Build()
File_weather_proto = out.File
file_weather_proto_goTypes = nil
file_weather_proto_depIdxs = nil
}
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v6.33.0
// source: weather.proto
package weather_system
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
WeatherAnalyticsService_StreamWeatherData_FullMethodName = "/weather_system.WeatherAnalyticsService/StreamWeatherData"
WeatherAnalyticsService_StreamAnalytics_FullMethodName = "/weather_system.WeatherAnalyticsService/StreamAnalytics"
WeatherAnalyticsService_GetLastReport_FullMethodName = "/weather_system.WeatherAnalyticsService/GetLastReport"
WeatherAnalyticsService_ControlSensor_FullMethodName = "/weather_system.WeatherAnalyticsService/ControlSensor"
)
// WeatherAnalyticsServiceClient is the client API for WeatherAnalyticsService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// 2. Weather Analytics Service (Python) - The central server
type WeatherAnalyticsServiceClient interface {
// 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.
StreamWeatherData(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[WeatherData, WeatherStats], error)
// B. Server Streaming (Required: Send to Dashboard)
// Dashboard requests (with Empty) a continuous stream of reports.
StreamAnalytics(ctx context.Context, in *Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[WeatherStats], error)
// C. Unary RPC (Optional Requirement: Retrieve last report)
// Dashboard manually requests the last stored stats.
GetLastReport(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*WeatherStats, error)
// D. Bidirectional Streaming (Academic addition)
// Allows the server to send immediate alerts and potentially control the sensor.
ControlSensor(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[AlertMessage, WeatherData], error)
}
type weatherAnalyticsServiceClient struct {
cc grpc.ClientConnInterface
}
func NewWeatherAnalyticsServiceClient(cc grpc.ClientConnInterface) WeatherAnalyticsServiceClient {
return &weatherAnalyticsServiceClient{cc}
}
func (c *weatherAnalyticsServiceClient) StreamWeatherData(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[WeatherData, WeatherStats], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &WeatherAnalyticsService_ServiceDesc.Streams[0], WeatherAnalyticsService_StreamWeatherData_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[WeatherData, WeatherStats]{ClientStream: stream}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type WeatherAnalyticsService_StreamWeatherDataClient = grpc.ClientStreamingClient[WeatherData, WeatherStats]
func (c *weatherAnalyticsServiceClient) StreamAnalytics(ctx context.Context, in *Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[WeatherStats], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &WeatherAnalyticsService_ServiceDesc.Streams[1], WeatherAnalyticsService_StreamAnalytics_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[Empty, WeatherStats]{ClientStream: stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type WeatherAnalyticsService_StreamAnalyticsClient = grpc.ServerStreamingClient[WeatherStats]
func (c *weatherAnalyticsServiceClient) GetLastReport(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*WeatherStats, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(WeatherStats)
err := c.cc.Invoke(ctx, WeatherAnalyticsService_GetLastReport_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *weatherAnalyticsServiceClient) ControlSensor(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[AlertMessage, WeatherData], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &WeatherAnalyticsService_ServiceDesc.Streams[2], WeatherAnalyticsService_ControlSensor_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[AlertMessage, WeatherData]{ClientStream: stream}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type WeatherAnalyticsService_ControlSensorClient = grpc.BidiStreamingClient[AlertMessage, WeatherData]
// WeatherAnalyticsServiceServer is the server API for WeatherAnalyticsService service.
// All implementations must embed UnimplementedWeatherAnalyticsServiceServer
// for forward compatibility.
//
// 2. Weather Analytics Service (Python) - The central server
type WeatherAnalyticsServiceServer interface {
// 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.
StreamWeatherData(grpc.ClientStreamingServer[WeatherData, WeatherStats]) error
// B. Server Streaming (Required: Send to Dashboard)
// Dashboard requests (with Empty) a continuous stream of reports.
StreamAnalytics(*Empty, grpc.ServerStreamingServer[WeatherStats]) error
// C. Unary RPC (Optional Requirement: Retrieve last report)
// Dashboard manually requests the last stored stats.
GetLastReport(context.Context, *Empty) (*WeatherStats, error)
// D. Bidirectional Streaming (Academic addition)
// Allows the server to send immediate alerts and potentially control the sensor.
ControlSensor(grpc.BidiStreamingServer[AlertMessage, WeatherData]) error
mustEmbedUnimplementedWeatherAnalyticsServiceServer()
}
// UnimplementedWeatherAnalyticsServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedWeatherAnalyticsServiceServer struct{}
func (UnimplementedWeatherAnalyticsServiceServer) StreamWeatherData(grpc.ClientStreamingServer[WeatherData, WeatherStats]) error {
return status.Errorf(codes.Unimplemented, "method StreamWeatherData not implemented")
}
func (UnimplementedWeatherAnalyticsServiceServer) StreamAnalytics(*Empty, grpc.ServerStreamingServer[WeatherStats]) error {
return status.Errorf(codes.Unimplemented, "method StreamAnalytics not implemented")
}
func (UnimplementedWeatherAnalyticsServiceServer) GetLastReport(context.Context, *Empty) (*WeatherStats, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetLastReport not implemented")
}
func (UnimplementedWeatherAnalyticsServiceServer) ControlSensor(grpc.BidiStreamingServer[AlertMessage, WeatherData]) error {
return status.Errorf(codes.Unimplemented, "method ControlSensor not implemented")
}
func (UnimplementedWeatherAnalyticsServiceServer) mustEmbedUnimplementedWeatherAnalyticsServiceServer() {
}
func (UnimplementedWeatherAnalyticsServiceServer) testEmbeddedByValue() {}
// UnsafeWeatherAnalyticsServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to WeatherAnalyticsServiceServer will
// result in compilation errors.
type UnsafeWeatherAnalyticsServiceServer interface {
mustEmbedUnimplementedWeatherAnalyticsServiceServer()
}
func RegisterWeatherAnalyticsServiceServer(s grpc.ServiceRegistrar, srv WeatherAnalyticsServiceServer) {
// If the following call pancis, it indicates UnimplementedWeatherAnalyticsServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&WeatherAnalyticsService_ServiceDesc, srv)
}
func _WeatherAnalyticsService_StreamWeatherData_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(WeatherAnalyticsServiceServer).StreamWeatherData(&grpc.GenericServerStream[WeatherData, WeatherStats]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type WeatherAnalyticsService_StreamWeatherDataServer = grpc.ClientStreamingServer[WeatherData, WeatherStats]
func _WeatherAnalyticsService_StreamAnalytics_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(Empty)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(WeatherAnalyticsServiceServer).StreamAnalytics(m, &grpc.GenericServerStream[Empty, WeatherStats]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type WeatherAnalyticsService_StreamAnalyticsServer = grpc.ServerStreamingServer[WeatherStats]
func _WeatherAnalyticsService_GetLastReport_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(WeatherAnalyticsServiceServer).GetLastReport(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: WeatherAnalyticsService_GetLastReport_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(WeatherAnalyticsServiceServer).GetLastReport(ctx, req.(*Empty))
}
return interceptor(ctx, in, info, handler)
}
func _WeatherAnalyticsService_ControlSensor_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(WeatherAnalyticsServiceServer).ControlSensor(&grpc.GenericServerStream[AlertMessage, WeatherData]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type WeatherAnalyticsService_ControlSensorServer = grpc.BidiStreamingServer[AlertMessage, WeatherData]
// WeatherAnalyticsService_ServiceDesc is the grpc.ServiceDesc for WeatherAnalyticsService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var WeatherAnalyticsService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "weather_system.WeatherAnalyticsService",
HandlerType: (*WeatherAnalyticsServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetLastReport",
Handler: _WeatherAnalyticsService_GetLastReport_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "StreamWeatherData",
Handler: _WeatherAnalyticsService_StreamWeatherData_Handler,
ClientStreams: true,
},
{
StreamName: "StreamAnalytics",
Handler: _WeatherAnalyticsService_StreamAnalytics_Handler,
ServerStreams: true,
},
{
StreamName: "ControlSensor",
Handler: _WeatherAnalyticsService_ControlSensor_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "weather.proto",
}
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