use crate::app::format::write::HeaderWriter;
use crate::app::parse::parser::{HeaderCollection, Response};
use crate::app::FunctionCode;
use crate::app::ResponseHeader;
use crate::link::EndpointAddress;
use crate::master::association::Association;
use crate::master::error::TaskError;
use crate::master::extract::extract_measurements;
use crate::master::handler::Promise;
use crate::master::poll::Poll;
use crate::master::request::{Classes, EventClasses};
use crate::master::tasks::auto::AutoTask;
use crate::master::tasks::command::CommandTask;
use crate::master::tasks::read::SingleReadTask;
use crate::master::tasks::restart::RestartTask;
use crate::master::tasks::time::TimeSyncTask;
use crate::master::{ReadType, TaskType};
use crate::master::tasks::deadbands::WriteDeadBandsTask;
use crate::master::tasks::empty_response::EmptyResponseTask;
use crate::master::tasks::file_read::FileReadTask;
use crate::master::tasks::get_file_info::GetFileInfoTask;
pub(crate) mod auto;
pub(crate) mod command;
pub(crate) mod deadbands;
pub(crate) mod empty_response;
pub(crate) mod file_read;
pub(crate) mod get_file_info;
pub(crate) mod read;
pub(crate) mod restart;
pub(crate) mod time;
pub(crate) struct AssociationTask {
pub(crate) address: EndpointAddress,
pub(crate) details: Task,
}
impl AssociationTask {
pub(crate) fn new(address: EndpointAddress, details: Task) -> Self {
Self { address, details }
}
}
pub(crate) enum Task {
Read(ReadTask),
NonRead(NonReadTask),
LinkStatus(Promise<Result<(), TaskError>>),
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub(crate) enum TaskId {
LinkStatus,
Function(FunctionCode),
}
impl Task {
pub(crate) fn on_task_error(self, association: Option<&mut Association>, err: TaskError) {
match self {
Task::NonRead(task) => task.on_task_error(association, err),
Task::Read(task) => task.on_task_error(association, err),
Task::LinkStatus(promise) => promise.complete(Err(err)),
}
}
pub(crate) fn start(self, association: &mut Association) -> Option<Task> {
if let Task::NonRead(task) = self {
return task.start(association).map(|task| task.wrap());
}
Some(self)
}
pub(crate) fn get_id(&self) -> TaskId {
match self {
Task::LinkStatus(_) => TaskId::LinkStatus,
Task::Read(_) => TaskId::Function(FunctionCode::Read),
Task::NonRead(t) => TaskId::Function(t.function()),
}
}
}
pub(crate) trait RequestWriter {
fn function(&self) -> FunctionCode;
fn write(&self, writer: &mut HeaderWriter) -> Result<(), TaskError>;
}
pub(crate) enum ReadTask {
PeriodicPoll(Poll),
StartupIntegrity(Classes),
EventScan(EventClasses),
SingleRead(SingleReadTask),
}
pub(crate) enum NonReadTask {
Auto(AutoTask),
Command(CommandTask),
TimeSync(TimeSyncTask),
Restart(RestartTask),
DeadBands(WriteDeadBandsTask),
EmptyResponseTask(EmptyResponseTask),
FileRead(FileReadTask),
GetFileInfo(GetFileInfoTask),
}
impl RequestWriter for ReadTask {
fn function(&self) -> FunctionCode {
FunctionCode::Read
}
fn write(&self, writer: &mut HeaderWriter) -> Result<(), TaskError> {
match self {
ReadTask::PeriodicPoll(poll) => poll.format(writer)?,
ReadTask::StartupIntegrity(classes) => classes.write(writer)?,
ReadTask::EventScan(classes) => classes.write(writer)?,
ReadTask::SingleRead(req) => req.format(writer)?,
}
Ok(())
}
}
impl RequestWriter for NonReadTask {
fn function(&self) -> FunctionCode {
self.function()
}
fn write(&self, writer: &mut HeaderWriter) -> Result<(), TaskError> {
match self {
NonReadTask::Auto(t) => t.write(writer)?,
NonReadTask::Command(t) => t.write(writer)?,
NonReadTask::TimeSync(t) => t.write(writer)?,
NonReadTask::Restart(_) => {}
NonReadTask::DeadBands(t) => t.write(writer)?,
NonReadTask::EmptyResponseTask(t) => t.write(writer)?,
NonReadTask::FileRead(t) => t.write(writer)?,
NonReadTask::GetFileInfo(t) => t.write(writer)?,
}
Ok(())
}
}
impl From<crate::app::format::WriteError> for TaskError {
fn from(_: crate::app::format::WriteError) -> Self {
TaskError::WriteError
}
}
impl ReadTask {
pub(crate) fn wrap(self) -> Task {
Task::Read(self)
}
pub(crate) async fn process_response(
&mut self,
association: &mut Association,
header: ResponseHeader,
objects: HeaderCollection<'_>,
) {
match self {
ReadTask::StartupIntegrity(_) => {
association.handle_integrity_response(header, objects).await
}
ReadTask::PeriodicPoll(_) => association.handle_poll_response(header, objects).await,
ReadTask::EventScan(_) => {
association
.handle_event_scan_response(header, objects)
.await
}
ReadTask::SingleRead(task) => match &mut task.custom_handler {
Some(handler) => {
extract_measurements(ReadType::SinglePoll, header, objects, handler.as_mut())
.await
}
None => association.handle_read_response(header, objects).await,
},
}
}
pub(crate) fn complete(self, association: &mut Association) {
match self {
ReadTask::StartupIntegrity(_) => association.on_integrity_scan_complete(),
ReadTask::PeriodicPoll(poll) => association.complete_poll(poll.id),
ReadTask::EventScan(_) => association.on_event_scan_complete(),
ReadTask::SingleRead(task) => task.on_complete(),
}
}
pub(crate) fn on_task_error(self, association: Option<&mut Association>, err: TaskError) {
match self {
ReadTask::StartupIntegrity(_) => {
if let Some(association) = association {
association.on_integrity_scan_failure();
}
}
ReadTask::PeriodicPoll(poll) => {
if let Some(association) = association {
tracing::warn!("poll {} failed", poll.id);
association.complete_poll(poll.id);
}
}
ReadTask::EventScan(_) => {
if let Some(association) = association {
association.on_event_scan_failure();
}
}
ReadTask::SingleRead(task) => task.on_task_error(err),
}
}
pub(crate) fn as_task_type(&self) -> TaskType {
match self {
Self::PeriodicPoll(_) => TaskType::PeriodicPoll,
Self::StartupIntegrity(_) => TaskType::StartupIntegrity,
Self::EventScan(_) => TaskType::AutoEventScan,
Self::SingleRead(_) => TaskType::UserRead,
}
}
}
impl NonReadTask {
pub(crate) fn wrap(self) -> Task {
Task::NonRead(self)
}
pub(crate) fn start(self, association: &mut Association) -> Option<NonReadTask> {
match self {
Self::Command(_) => Some(self),
Self::Auto(_) => Some(self),
Self::TimeSync(task) => task.start(association).map(|task| task.wrap()),
Self::Restart(_) => Some(self),
Self::DeadBands(_) => Some(self),
Self::EmptyResponseTask(_) => Some(self),
Self::FileRead(_) => Some(self),
Self::GetFileInfo(_) => Some(self),
}
}
pub(crate) fn function(&self) -> FunctionCode {
match self {
Self::Command(task) => task.function(),
Self::Auto(task) => task.function(),
Self::TimeSync(task) => task.function(),
Self::Restart(task) => task.function(),
Self::DeadBands(task) => task.function(),
Self::EmptyResponseTask(task) => task.function(),
Self::FileRead(task) => task.function(),
Self::GetFileInfo(task) => task.function(),
}
}
pub(crate) fn on_task_error(self, association: Option<&mut Association>, err: TaskError) {
match self {
Self::Command(task) => task.on_task_error(err),
Self::TimeSync(task) => task.on_task_error(association, err),
Self::Auto(task) => task.on_task_error(association, err),
Self::Restart(task) => task.on_task_error(err),
Self::DeadBands(task) => task.on_task_error(err),
Self::EmptyResponseTask(task) => task.on_task_error(err),
Self::FileRead(task) => task.on_task_error(err),
Self::GetFileInfo(task) => task.on_task_error(err),
}
}
pub(crate) async fn handle(
self,
association: &mut Association,
response: Response<'_>,
) -> Option<NonReadTask> {
match self {
Self::Command(task) => task.handle(response),
Self::Auto(task) => match response.objects.ok() {
Some(headers) => task.handle(association, response.header, headers),
None => None,
},
Self::TimeSync(task) => task.handle(association, response),
Self::Restart(task) => task.handle(response),
Self::DeadBands(task) => task.handle(response),
Self::EmptyResponseTask(task) => task.handle(response),
Self::FileRead(task) => task.handle(response).await,
Self::GetFileInfo(task) => task.handle(response),
}
}
pub(crate) fn as_task_type(&self) -> TaskType {
match self {
Self::Command(_) => TaskType::Command,
Self::Auto(x) => match x {
AutoTask::ClearRestartBit => TaskType::ClearRestartBit,
AutoTask::EnableUnsolicited(_) => TaskType::EnableUnsolicited,
AutoTask::DisableUnsolicited(_) => TaskType::DisableUnsolicited,
},
Self::TimeSync(_) => TaskType::TimeSync,
Self::Restart(_) => TaskType::Restart,
Self::DeadBands(_) => TaskType::WriteDeadBands,
Self::EmptyResponseTask(_) => TaskType::GenericEmptyResponse(self.function()),
Self::FileRead(_) => TaskType::FileRead,
Self::GetFileInfo(_) => TaskType::GetFileInfo,
}
}
}