Fires
This application demonstrates a simulation of a schedule of fires given geospatial locations and specified datetimes (at one minute resolution)
The application contains a single Environment class which listens to the time status published by the manager application and publishes fire information at the specified ignition datetime. The application also contains callback messages that updates datetime in the fires DataFrame for each of ignition (including latitude-longitude GeographicPosition), detection, and reporting.
Class
- class Environment(app, fires)
Bases:
ObserverThe Environment object class inherits properties from the Observer object class in the NOS-T tools library
- app
An application containing a test-run namespace, a name and description for the app, client credentials, and simulation timing instructions
- Type:
ManagedApplication
- Environment.on_change(source, property_name, old_value, new_value)
Standard on_change callback function format inherited from Observer object class
In this instance, the callback function checks the simulation
datetimeagainst each scheduled fire ignitiondatetimefor the scenario. If past the scheduled start of a fire, aFireStartedmessage is sent to PREFIX.fire.location:if property_name == "time": new_fires = self.fires[ (self.fires.start <= new_value) & (self.fires.start > old_value) ] for index, fire in new_fires.iterrows(): self.app.send_message( self.app.app_name, "location", FireStarted( fireId=fire.fireId, start=fire.start, latitude=fire.latitude, longitude=fire.longitude, ).model_dump_json(), )
Callback Functions
- main_fire.on_fire(method, properties, body)
Callback function parses a FireStarted message and switches FireState from “undefined” to “started”
def on_fire(self, ch, method, properties, body): body = body.decode("utf-8") start = FireStarted.model_validate_json(body) for key, fire in self.fires.iterrows(): if key == start.fireId: self.fires["fireState"][key] = FireState.started break
- main_fire.on_detected(method, properties, body)
Callback function parses a FireDetected message, switches FireState from “started” to “detected”, and records time of first detection and name of satellite detecting the fire
def on_detected(self, ch, method, properties, body): body = body.decode("utf-8") detect = FireDetected.model_validate_json(body) for key, fire in self.fires.iterrows(): if key == detect.fireId: self.fires["fireState"][key] = FireState.detected self.fires["detected"][key] = detect.detected self.fires["detected_by"][key] = detect.detected_by break
- main_fire.on_reported(method, properties, body)
Callback function parses a FireReported message, switches FireState from “detected” to “reported”, and records time of first report, name of satellite reporting the fire, and groundId receiving the report
def on_reported(self, ch, method, properties, body): body = body.decode("utf-8") report = FireReported.model_validate_json(body) for key, fire in self.fires.iterrows(): if key == report.fireId: self.fires["fireState"][key] = FireState.reported self.fires["reported"][key] = report.reported self.fires["reported_by"][key] = report.reported_by self.fires["reported_to"][key] = report.reported_to break
Schema
Schema are implemented using the pydantic library. The following schema define consistent message structures between this application and other observer applications:
- pydantic settings FireStarted
Bases:
BaseModelMessage schema object class with properties inherited from the pydantic library’s BaseModel
Standardized message for fire ignition includes:
Show JSON schema
{ "title": "FireStarted", "description": "*Message schema object class with properties inherited from the pydantic library's BaseModel*\n\nStandardized message for fire ignition includes:", "type": "object", "properties": { "fireId": { "description": "Unique fire identifier.", "title": "Fireid", "type": "integer" }, "start": { "anyOf": [ { "format": "date-time", "type": "string" }, { "type": "null" } ], "description": "Time fire started.", "title": "Start" }, "latitude": { "anyOf": [ { "maximum": 90, "minimum": -90, "type": "number" }, { "type": "null" } ], "description": "Latitude (deg) of fire location.", "title": "Latitude" }, "longitude": { "anyOf": [ { "maximum": 180, "minimum": -180, "type": "number" }, { "type": "null" } ], "description": "Longitude (deg) of fire location.", "title": "Longitude" } }, "required": [ "fireId", "start", "latitude", "longitude" ] }
- Fields:
fireId (int)start (datetime.datetime | None)latitude (float | None)longitude (float | None)
- field fireId: int [Required]
Unique fire identifier.
- pydantic settings FireDetected
Bases:
BaseModelMessage schema object class with properties inherited from the pydantic library’s BaseModel
Standardized message for fire detection includes:
Show JSON schema
{ "title": "FireDetected", "description": "*Message schema object class with properties inherited from the pydantic library's BaseModel*\n\nStandardized message for fire detection includes:", "type": "object", "properties": { "fireId": { "description": "Unique fire identifier.", "title": "Fireid", "type": "integer" }, "detected": { "description": "Time fire detected.", "format": "date-time", "title": "Detected", "type": "string" }, "detected_by": { "description": "Satellite name that detected the fire.", "title": "Detected By", "type": "string" } }, "required": [ "fireId", "detected", "detected_by" ] }
- Fields:
fireId (int)detected (datetime.datetime)detected_by (str)
- field fireId: int [Required]
Unique fire identifier.
- field detected: datetime [Required]
Time fire detected.
- field detected_by: str [Required]
Satellite name that detected the fire.
- pydantic settings FireReported
Bases:
BaseModelMessage schema object class with properties inherited from the pydantic library’s BaseModel
Standardized message for fire reporting includes:
Show JSON schema
{ "title": "FireReported", "description": "*Message schema object class with properties inherited from the pydantic library's BaseModel*\n\nStandardized message for fire reporting includes:", "type": "object", "properties": { "fireId": { "description": "Unique fire identifier.", "title": "Fireid", "type": "integer" }, "reported": { "description": "Time fire reported.", "format": "date-time", "title": "Reported", "type": "string" }, "reported_by": { "description": "Satellite name that sent the fire report.", "title": "Reported By", "type": "string" }, "reported_to": { "description": "Station id that received the fire report.", "title": "Reported To", "type": "integer" } }, "required": [ "fireId", "reported", "reported_by", "reported_to" ] }
- Fields:
fireId (int)reported (datetime.datetime)reported_by (str)reported_to (int)
- field fireId: int [Required]
Unique fire identifier.
- field reported: datetime [Required]
Time fire reported.
- field reported_by: str [Required]
Satellite name that sent the fire report.
- field reported_to: int [Required]
Station id that received the fire report.
Startup Script
The following code demonstrates how the fires application is started up, how the Environment Observer object class is initialized and added to the simulator, and how the callback functions are added:
# Define application name
NAME = "fire"
# Load config
config = ConnectionConfig(yaml_file="firesat.yaml", app_name=NAME)
# Create the managed application
app = ManagedApplication(app_name=NAME)
# Import CSV file from fire_scenarios subdirectory with scenario defining locations and ignition datetimes of fires
csvFile = os.path.join("fires", "fire_scenarios", "first5days.csv")
# Read the csv file and convert to a DataFrame with initial column defining the index
df = pd.read_csv(csvFile, index_col=0)
fires = pd.DataFrame(
data={
"fireId": df.index,
"start": pd.to_datetime(df["start_time"], utc=True),
"latitude": df["latitude"],
"longitude": df["longitude"],
}
)
# Add blank columns to data frame for logging state, detection time, reporting time, and detector satellite
fires.insert(1, "fireState", FireState.undefined)
fires.insert(3, "detected", datetime(1900, 1, 1, tzinfo=timezone.utc))
fires.insert(4, "detected_by", "Undetected")
fires.insert(5, "reported", datetime(1900, 1, 1, tzinfo=timezone.utc))
fires.insert(6, "reported_by", "Unreported")
fires.insert(7, "reported_to", None)
# Add the environment observer to monitor for fire status events
app.simulator.add_observer(Environment(app, fires))
# Add a shutdown observer to shut down after a single test case
app.simulator.add_observer(ShutDownObserver(app))
# Start up the application
app.start_up(
config.rc.simulation_configuration.execution_parameters.general.prefix,
config,
)
# Add message callbacks
app.add_message_callback("fire", "location", on_fire)
app.add_message_callback("constellation", "detected", on_detected)
app.add_message_callback("constellation", "reported", on_reported)
while True:
pass