Model typed SA ports and atomic exchange allocation workflows
This commit is contained in:
+105
-12
@@ -6,7 +6,7 @@ use std::{
|
||||
use uuid::Uuid;
|
||||
|
||||
pub type Id = Uuid;
|
||||
pub const SCHEMA_VERSION: u32 = 2;
|
||||
pub const SCHEMA_VERSION: u32 = 3;
|
||||
pub const MAX_COORDINATE: f32 = 1_000_000.;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
@@ -293,6 +293,54 @@ pub enum PortDirection {
|
||||
InOut,
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum ComponentPortKind {
|
||||
Standard,
|
||||
Flow,
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum ComponentPortOrientation {
|
||||
Unset,
|
||||
In,
|
||||
Out,
|
||||
InOut,
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct LegacyPortContract {
|
||||
pub direction: PortDirection,
|
||||
pub interface: Option<Id>,
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub enum PortContract {
|
||||
FunctionInput {
|
||||
incoming_items: Vec<Id>,
|
||||
},
|
||||
FunctionOutput {
|
||||
outgoing_items: Vec<Id>,
|
||||
},
|
||||
Component {
|
||||
kind: ComponentPortKind,
|
||||
orientation: ComponentPortOrientation,
|
||||
},
|
||||
Legacy,
|
||||
}
|
||||
impl PortContract {
|
||||
pub fn items(&self) -> &[Id] {
|
||||
match self {
|
||||
Self::FunctionInput { incoming_items } => incoming_items,
|
||||
Self::FunctionOutput { outgoing_items } => outgoing_items,
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
pub fn is_function(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::FunctionInput { .. } | Self::FunctionOutput { .. }
|
||||
)
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum StateKind {
|
||||
State,
|
||||
Mode,
|
||||
@@ -468,8 +516,8 @@ pub enum SharedBody {
|
||||
items: Vec<Id>,
|
||||
},
|
||||
Port {
|
||||
direction: PortDirection,
|
||||
interface: Option<Id>,
|
||||
contract: PortContract,
|
||||
legacy: Option<LegacyPortContract>,
|
||||
},
|
||||
Scenario {
|
||||
#[serde(default)]
|
||||
@@ -635,10 +683,8 @@ impl ElementBody {
|
||||
unit: String::new(),
|
||||
},
|
||||
Kind::Interface => SharedBody::Interface { items: vec![] },
|
||||
Kind::Port => SharedBody::Port {
|
||||
direction: PortDirection::InOut,
|
||||
interface: None,
|
||||
},
|
||||
// Port creation requires an explicit contract and owner.
|
||||
Kind::Port => return None,
|
||||
Kind::Scenario => SharedBody::Scenario {
|
||||
viewpoint: if layer == Layer::Oa {
|
||||
ScenarioViewpoint::OperationalActivity
|
||||
@@ -707,6 +753,7 @@ impl SharedBody {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum RelationKind {
|
||||
Allocation,
|
||||
PortAllocation,
|
||||
Interaction,
|
||||
CapabilityInvolvement,
|
||||
ChainInvolvement,
|
||||
@@ -728,8 +775,9 @@ pub enum RelationKind {
|
||||
StateAction,
|
||||
}
|
||||
impl RelationKind {
|
||||
pub const ALL: [Self; 20] = [
|
||||
pub const ALL: [Self; 21] = [
|
||||
Self::Allocation,
|
||||
Self::PortAllocation,
|
||||
Self::Interaction,
|
||||
Self::CapabilityInvolvement,
|
||||
Self::ChainInvolvement,
|
||||
@@ -753,6 +801,7 @@ impl RelationKind {
|
||||
pub fn permits(self, from: Kind, to: Kind) -> bool {
|
||||
match self {
|
||||
Self::Allocation => from.is_performer() && to.is_work(),
|
||||
Self::PortAllocation => from == Kind::Port && to == Kind::Port,
|
||||
Self::Interaction => from == Kind::Activity && to == Kind::Activity,
|
||||
Self::CapabilityInvolvement => {
|
||||
(from.is_performer() || from.is_work() || from == Kind::Role)
|
||||
@@ -807,6 +856,7 @@ impl RelationKind {
|
||||
pub fn hint(self) -> &'static str {
|
||||
match self {
|
||||
Self::Allocation => "Performer → leaf activity/function",
|
||||
Self::PortAllocation => "Component port → allocated function port",
|
||||
Self::Interaction => "OA activity → OA activity",
|
||||
Self::CapabilityInvolvement => "Participant/activity/function → capability",
|
||||
Self::ChainInvolvement => "Process/chain → capability",
|
||||
@@ -837,6 +887,7 @@ impl fmt::Display for RelationKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match self {
|
||||
Self::Allocation => "Allocation",
|
||||
Self::PortAllocation => "Port allocation",
|
||||
Self::Interaction => "Interaction",
|
||||
Self::CapabilityInvolvement => "Capability involvement",
|
||||
Self::ChainInvolvement => "Chain involvement",
|
||||
@@ -875,6 +926,10 @@ pub enum RelationBody {
|
||||
source: Id,
|
||||
target: Id,
|
||||
},
|
||||
PortAllocation {
|
||||
source: Id,
|
||||
target: Id,
|
||||
},
|
||||
Interaction {
|
||||
source: Id,
|
||||
target: Id,
|
||||
@@ -899,13 +954,14 @@ pub enum RelationBody {
|
||||
FunctionalExchange {
|
||||
source: Id,
|
||||
target: Id,
|
||||
source_port: Option<Id>,
|
||||
target_port: Option<Id>,
|
||||
source_port: Id,
|
||||
target_port: Id,
|
||||
items: Vec<Id>,
|
||||
},
|
||||
BoundaryExchange {
|
||||
source: Id,
|
||||
target: Id,
|
||||
oriented: bool,
|
||||
items: Vec<Id>,
|
||||
},
|
||||
MissionCapability {
|
||||
@@ -984,6 +1040,7 @@ impl RelationBody {
|
||||
pub fn kind(&self) -> RelationKind {
|
||||
match self {
|
||||
Self::Allocation { .. } => RelationKind::Allocation,
|
||||
Self::PortAllocation { .. } => RelationKind::PortAllocation,
|
||||
Self::Interaction { .. } => RelationKind::Interaction,
|
||||
Self::CapabilityInvolvement { .. } => RelationKind::CapabilityInvolvement,
|
||||
Self::ChainInvolvement { .. } => RelationKind::ChainInvolvement,
|
||||
@@ -1008,6 +1065,7 @@ impl RelationBody {
|
||||
pub fn endpoints(&self) -> (Id, Id) {
|
||||
match self {
|
||||
Self::Allocation { source, target }
|
||||
| Self::PortAllocation { source, target }
|
||||
| Self::Interaction { source, target, .. }
|
||||
| Self::CapabilityInvolvement { source, target }
|
||||
| Self::ChainInvolvement { source, target }
|
||||
@@ -1032,6 +1090,7 @@ impl RelationBody {
|
||||
pub fn set_endpoints(&mut self, a: Id, b: Id) {
|
||||
match self {
|
||||
Self::Allocation { source, target }
|
||||
| Self::PortAllocation { source, target }
|
||||
| Self::Interaction { source, target, .. }
|
||||
| Self::CapabilityInvolvement { source, target }
|
||||
| Self::ChainInvolvement { source, target }
|
||||
@@ -1067,6 +1126,7 @@ impl RelationBody {
|
||||
pub fn new(kind: RelationKind, source: Id, target: Id) -> Self {
|
||||
match kind {
|
||||
RelationKind::Allocation => Self::Allocation { source, target },
|
||||
RelationKind::PortAllocation => Self::PortAllocation { source, target },
|
||||
RelationKind::Interaction => Self::Interaction {
|
||||
source,
|
||||
target,
|
||||
@@ -1079,13 +1139,15 @@ impl RelationBody {
|
||||
RelationKind::FunctionalExchange => Self::FunctionalExchange {
|
||||
source,
|
||||
target,
|
||||
source_port: None,
|
||||
target_port: None,
|
||||
// Unbound drafts must be completed before candidate admission.
|
||||
source_port: Id::nil(),
|
||||
target_port: Id::nil(),
|
||||
items: vec![],
|
||||
},
|
||||
RelationKind::BoundaryExchange => Self::BoundaryExchange {
|
||||
source,
|
||||
target,
|
||||
oriented: true,
|
||||
items: vec![],
|
||||
},
|
||||
RelationKind::MissionCapability => Self::MissionCapability { source, target },
|
||||
@@ -1584,6 +1646,37 @@ impl Project {
|
||||
pub fn relate(&mut self, kind: RelationKind, name: &str, source: Id, target: Id) -> Id {
|
||||
let id = Id::new_v4();
|
||||
let alias = self.fresh_alias(name, "relation", id);
|
||||
if kind == RelationKind::FunctionalExchange
|
||||
&& self.elements.contains_key(&source)
|
||||
&& self.elements.contains_key(&target)
|
||||
{
|
||||
let request = crate::ports::FunctionalExchangeRequest {
|
||||
id,
|
||||
alias,
|
||||
name: name.into(),
|
||||
description: String::new(),
|
||||
source,
|
||||
target,
|
||||
source_port: crate::ports::PortChoice::Auto,
|
||||
target_port: crate::ports::PortChoice::Auto,
|
||||
items: vec![],
|
||||
};
|
||||
// Project is the unchecked builder; Session validates the complete batch.
|
||||
for command in crate::ports::functional_exchange_commands(self, &request)
|
||||
.expect("fresh exchange identities")
|
||||
{
|
||||
match command {
|
||||
crate::Command::CreateElement(e) => {
|
||||
self.elements.insert(e.id, e);
|
||||
}
|
||||
crate::Command::CreateRelation(r) => {
|
||||
self.relations.insert(r.id, r);
|
||||
}
|
||||
_ => unreachable!("functional creation only creates ports and an exchange"),
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
let mut body = RelationBody::new(kind, source, target);
|
||||
if self
|
||||
.elements
|
||||
|
||||
Reference in New Issue
Block a user