Skip to main content

Attribute

Port uses C# attributes to declaratively define package registration, API generation, dependency injection, and workflow control. This page is a categorized reference for all Port attributes.


Quick Reference

Summary table of all Port attributes.

CategoryAttributeTargetDescription
Portdic[Portdic]classRegister main window as Port project entry point
Package[Package]classRegister Port-managed package
[Handler]propertyInject IPackageHandler (log + property)
[Preset]methodInitialization after injection
[API]property, fieldGenerate REST API endpoint
[Valid]methodValidation gate
[Comment]propertyAPI documentation
Controller[Controller]classFlow controller
[Flow]classWorkflow definition
[FlowStep]methodWorkflow step
[Handler]propertyInject IFlowHandler (basic step control)
[Handler]propertyInject IFlowWithModelHandler<T> (lifecycle events with model)
[Handler]propertyInject ISchedulerHandler<T> (transfer scheduling)
[Model]classDefine flow model class
[ModelBinding]propertyBind model property to a Port entry
[FlowWatcherCompare]methodStep condition watcher
[FlowWatcherAction]methodStep completion action
[Timeout]methodStep timeout
Document[Document]classSource document path
[Save]methodOutput file paths
[ColumnHeader]propertyColumn header mapping
[EntryKey]propertyKey column
[EntryProperty]propertyProperty column
[EntryDataType]propertyData type

Portdic

[Portdic("repoName")] is the entry-point attribute. It must be applied to the application's main class (e.g., MainWindow) and registers the project under the given repository name. All Port services, packages, controllers, and flows are resolved within this project scope.

The repoName string must match the name used in .page file paths and Port.Push() / Port.Add() calls throughout the project.

Constructor:

Portdic(string reponame, string pull_path = "")
ParameterTypeRequiredDescription
reponamestringYesRepository name — must match all Port.Add() / Port.Push() calls in the project
pull_pathstringNoLocal directory path for pulling remote data; defaults to "" (disabled)
[Portdic("EQ-01-B05")]
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Port.App<MainWindow>();

Port.Add<SessionHelper>("Session");
Port.Add<LoadportController>("LP1");
Port.Add<LoadportController>("LP2");
Port.Add<WTRController>("WTR");
}
}

Package Attributes

Attributes used for defining package classes and generating APIs.

AttributeTargetInjected TypeArgumentsDescription
[Package]classRegisters class as a Port-managed package
[Handler]propertyIPackageHandlerInjects handler — unified access to logging and entry property
[Preset]methodCalled once after injection; use for initialization and event wiring
[API]property, fieldEntryDataType, PropertyFormat, keysGenerates REST API endpoint
[Valid]methodmessageValidation gate; returns error message when invalid
[Comment]propertystringAPI property documentation

Package

Registers a class as a managed package in the Port Dictionary system.

Constructor: No parameters.

Package()
[Package]
public class Bulb
{
[Handler]
public IPackageHandler Handler { get; set; }

[API(EntryDataType.Enum)]
public string OffOn { get; set; } = "Off";
}

Handler

Injects IPackageHandler, which provides unified access to logging and entry property values. Use SetLogger in [Preset] to enable file logging, then call Write anywhere inside the package.

Constructor: No parameters — applies to both [Handler] and [Preset].

Handler()
Preset()
[Package]
public class Heater
{
[Handler]
public IPackageHandler Handler { get; set; }

[Preset]
public void Preset()
{
// Enable file logging — writes to C:\Logs\Heater\
Handler.SetLogger(@"C:\Logs");
}

[API]
public double Temp
{
get
{
// Read entry property (set in .page via property:{...})
if (Handler.EntryProperty.TryToGetValue("Unit", out string unit))
{
Handler.Write($"[INFO] Unit={unit}");
return unit == "F" ? 212.0 : 100.0;
}
return double.NaN;
}
}
}

IPackageHandler members:

MemberDescription
SetLogger(rootPath)Enable file logging; creates rootPath/packageName/ sub-directory
SetLogger(rootPath, conf)Same with rotation and retention options via PortLogConfiguration
Write(message)Write a plain text log entry
Write(code, header, dict)Write a structured log entry with LogTypeCode, header, and key-value data
EntryPropertyCurrent IProperty for the active Get/Set request; use TryToGetValue to read values

IProperty.TryToGetValue usage:

// .page entry definition
// RoomTemp f8 property:{"Unit":"C","Max":"300"}

// Inside a package API property getter:
if (Handler.EntryProperty.TryToGetValue("Unit", out string unit))
{
// unit == "C"
}
if (Handler.EntryProperty.TryToGetValue("Max", out string max))
{
double limit = double.Parse(max); // limit == 300.0
}

API

Automatically registers a property as a REST API endpoint. Specify the data type with EntryDataType.

Constructors:

API()
API(EntryDataType dataType)
API(string key)
API(EntryDataType dataType, PropertyFormat format)
API(EntryDataType dataType, PropertyFormat format, params string[] required)
API(EntryDataType dataType, PropertyFormat format, params int[] required)
ParameterTypeDescription
dataTypeEntryDataTypeSECS-compatible data type of the exposed property (default: Text)
keystringCustom entry key name; use when the property name differs from the entry key
formatPropertyFormatSerialization format for multi-value properties (Json, Array, etc.)
requiredstring[] or int[]Required property keys (strings) or required index list (ints) for structured types
// Basic usage
[API]
public string Status { get; set; }

// Enum type
[API(EntryDataType.Enum)]
public string OffOn { get; set; }

// Numeric with JSON property keys
[API(EntryDataType.Num, PropertyFormat.Json, "Unit")]
public double Temp1 { get; set; }

// String type
[API(EntryDataType.Char)]
public string Power { get; set; }

// List type
[API(EntryDataType.List, PropertyFormat.Array, 0, 1, 2)]
public List<string> Readings { get; set; }

EntryDataType values:

TypeDescription
EntryDataType.TextText
EntryDataType.NumNumeric (double)
EntryDataType.CharASCII string
EntryDataType.EnumEnumeration
EntryDataType.ListList

Linking an API property to a Package in .page:

Use pkg:PackageName.PropertyName in the .page file to connect an entry to a [API] property in a [Package] class.

# device/io.page
RoomTemp f8 pkg:Heater.Temp
BulbStatus enum pkg:Bulb.OffOn
Power char pkg:Bulb.Power
// Package class — property names must match the pkg: declaration
[Package]
public class Heater
{
[API]
public double Temp { get; set; } // → pkg:Heater.Temp
}

[Package]
public class Bulb
{
[API(EntryDataType.Enum)]
public string OffOn { get; set; } // → pkg:Bulb.OffOn

[API(EntryDataType.Char)]
public string Power { get; set; } // → pkg:Bulb.Power
}

Valid

Defines a validation method for the package. When it returns false, the error message passed as argument is displayed.

Constructor:

Valid(string invalidComment)
ParameterTypeDescription
invalidCommentstringError message returned to the caller when the method returns false
[Valid("Device not connected")]
public bool Valid()
{
return serialPort.IsOpen;
}

Comment

Adds a description to an API property.

Constructors:

Comment(string comment)
Comment(Dictionary<string, string> comment)
ParameterTypeDescription
commentstringSingle description string for the property
commentDictionary<string, string>Multi-language or multi-key description map
[API, Comment("Current temperature (Celsius)")]
public double Temperature { get; set; }

Controller Attributes

Attributes used for defining and controlling workflows (process flows).

AttributeTargetInjected TypeArgumentsDescription
[Controller]classDefines a controller containing flows
[Flow]classkeyDefines a workflow class (inner class of Controller)
[FlowStep]methodindex, relatedEntryDefines a workflow step
[Handler]propertyIFlowHandlerInjects basic flow step control into a Flow class
[Handler]propertyIFlowWithModelHandler<T>Injects model-bound handler with lifecycle events
[Handler]propertyISchedulerHandler<T>Injects transfer scheduler handler
[Model]classDefines a flow model class; properties use [ModelBinding]
[ModelBinding]propertyEntrycontrollerName, entryKeyBinds a model property to a Port entry
[FlowWatcherCompare]methodentry, op, valueDefines step execution condition
[FlowWatcherAction]methodentry, valueDefines action on step completion
[Timeout]methodms, controller, alarmidSets step timeout and alarm

Controller

[Controller] defines a controller that contains multiple flows. All [Flow] classes must be declared as inner classes inside a [Controller] class.

Constructors:

Controller()
Flow(string key)
AttributeParameterTypeDescription
[Controller]No parameters; marks the class as a Port flow controller
[Flow]keystringFlow name — used to identify and trigger the flow at runtime
// Model class — defined outside the controller
[Model]
public class LoadportModel
{
[ModelBinding(Controller.LP1, EFEM.LP1_Command)]
[ModelBinding(Controller.LP2, EFEM.LP2_Command)]
public Entry LP_Command { get; set; }

[ModelBinding(Controller.LP1, EFEM.LP1_Main_Air_i)]
[ModelBinding(Controller.LP2, EFEM.LP2_Main_Air_i)]
public Entry LP_Main_Air_i { get; set; }
}

// Controller — [Flow] classes must be inner classes
[Controller]
public class LoadportController
{
[Flow("Load")]
public class FoupLoadFlow
{
[Handler]
public IFlowHandler Handler { get; set; } = null!;

[FlowStep(0)]
public void StatusCheck(LoadportModel m)
{
Handler.ClearAlarm(-1);
Debug.WriteLine(m.LP_Main_Air_i.Name + " : " + m.LP_Main_Air_i.Value.String());
Handler.Next();
}

[FlowStep(1)]
public void SendLoadCommand(LoadportModel m)
{
Handler.Next();
}

[FlowStep(2)]
public void Done(LoadportModel m)
{
Handler.Done();
}
}
}

Handler

Injects a handler interface into a [Flow] class. The injected type is determined by the property's declared type.

Property typePurpose
IFlowHandlerBasic step control (Next(), Done(), logging)
IFlowWithModelHandler<T>Lifecycle events that carry the bound model
ISchedulerHandler<T>Transfer scheduling for robot arm coordination

FlowStep methods always receive the model as a method parameter — they do not inject it as a property.

Constructor: No parameters — applies to [Handler] and [Preset].

Handler()
Preset()
// [Handler] — basic step control
[Flow("Load")]
public class FoupLoadFlow
{
[Handler]
public IFlowHandler Handler { get; set; } = null!;

[Preset]
public void Preset()
{
Handler.SetLogger(@"D:\logs");
}

[FlowStep(0)]
public void StatusCheck(LoadportModel m)
{
Handler.ClearAlarm(-1);
Handler.Next();
}

[FlowStep(1)]
public void Done(LoadportModel m)
{
Handler.Done();
}
}

// [Handler] — lifecycle events with model
//
// OnFlowFinished fires after handler.Done() completes.
// e.Model carries the bound model so the caller can act on the result
// (e.g. notify a scheduler that this transfer location is free).
[Flow("Place")]
public class Place
{
[Handler]
public IFlowWithModelHandler<WTRCommModel> handler { get; set; } = null!;

[Handler]
public ISchedulerHandler<DualArmActionArgs> scheduler { get; set; } = null!;

[Preset]
public void Preset()
{
handler.SetLogger(@"D:\logs");
handler.OnFlowOccured += Handler_OnFlowOccured;
handler.OnFlowFinished += Handler_OnFlowFinished;
}

// Called when the flow is triggered (before Step 0 runs).
// e.Model is the bound model populated at flow start.
private void Handler_OnFlowOccured(object sender, PortFlowOccuredWithModelArgs<WTRCommModel> e)
{
// e.Model.Target, e.Model.Source, etc. are already populated
}

// Called after handler.Done() returns the flow to Idle.
// Use e.Model to read the final state and notify downstream systems.
private void Handler_OnFlowFinished(object sender, FlowFinishedWithModelArgs<WTRCommModel> e)
{
// e.Model.Target contains the destination that was set when the flow was triggered
scheduler.TransferCompleted(e.Model.Target);
}

[FlowStep(0)]
public void CheckAction(WTRCommModel m)
{
handler.WriteLog("CheckAction", WriteRule.NotAllowDuplicate | WriteRule.WithDebug);
handler.Next();
}

[FlowStep(1)]
public void Done(WTRCommModel m)
{
handler.Done(); // triggers Handler_OnFlowFinished after returning to Idle
}
}

IFlowHandler members:

MemberDescription
Next()Move to the next FlowStep
Done()Complete the flow and return to Idle synchronously
Move(stepName)Jump to a named step
Alert(message)Send an alert notification
OccuredAlarm(alid)Raise an alarm by alarm ID
ClearAlarm(alid = -9999)Clear alarm; -9999 clears all alarms
SetLogger(rootPath)Enable file logging under rootPath/flowName/
WriteLog(message)Write a log entry
WriteLog(message, rule)Write a log entry with WriteRule flags

WriteRule flags:

FlagDescription
NoneWrite to file only (default)
NotAllowDuplicateSkip consecutive duplicate messages
WithDebugAlso write to Debug.WriteLine
WithConsoleAlso write to Console.WriteLine
WithTraceAlso write to Trace.WriteLine

IFlowWithModelHandler<T> additional members:

MemberDescription
T ModelThe model instance bound to this flow
OnFlowOccuredFired when the flow starts
OnFlowFinishedFired when the flow completes; e.Model carries the bound model

FlowStep

Registers a method as a workflow step. Steps are ordered by index number. The bound model is received as a method parameter — omit it if no model is needed.

Constructors:

FlowStep(int index = 0, params string[] relatedEntry)
FlowStep(string prev)
FlowStep(int index, ushort ceid, params string[] relatedEntry)
OverloadParameterTypeDescription
DefaultindexintStep execution order; lower numbers run first (default: 0)
DefaultrelatedEntrystring[]Entry keys that trigger step re-evaluation on value change
Named-prevprevstringName of the preceding step; use when step order is defined by name instead of index
CEIDindexintStep execution order
CEIDceidushortCollection Event ID to fire when this step completes
CEIDrelatedEntrystring[]Entry keys that trigger step re-evaluation
// With model parameter
[FlowStep(0)]
public void StatusCheck(LoadportModel m)
{
Debug.WriteLine(m.LP_Main_Air_i.Value.String());
Handler.Next();
}

[FlowStep(1)]
public void SendCommand(LoadportModel m)
{
Handler.Next();
}

[FlowStep(2)]
public void Done(LoadportModel m)
{
Handler.Done(); // Complete flow and return to Idle
}

FlowWatcherCompare

Defines pre/post conditions for a step. All conditions must be met before proceeding to the next step.

Constructors:

FlowWatcherCompare(string a, string op, object b, bool OR = false)
FlowWatcherCompare(string controller_name, string model_key_name, string op, object value, bool OR = false)
OverloadParameterTypeDescription
DirectastringLeft-hand entry key; prefix @ to reference a model binding
DirectopstringComparison operator: >=, <=, >, <, ==, !=
DirectbobjectRight-hand value or entry key
DirectORbooltrue = combine with previous watcher using OR logic (default: false = AND)
Controllercontroller_namestringLimit this watcher to the named controller instance (e.g. "LP1")
Controllermodel_key_namestringEntry key from the controller's model
ControlleropstringComparison operator
ControllervalueobjectRight-hand comparison value
ControllerORboolOR logic flag (default: false)
[FlowStep]
[FlowWatcherCompare("@Temp1", ">=", 50)] // Model binding (@)
[FlowWatcherCompare("room2.HeaterTemp4", ">=", 100)] // Direct reference
[FlowWatcherCompare("room2.HeaterTemp5", ">=", Room2.HeaterTemp4)] // Entry-to-entry comparison
public void Step1()
{
Handler.Next();
}

Supported operators: >=, <=, >, <, ==, !=

FlowWatcherAction

Automatically sets a value when a step completes.

Constructors:

FlowWatcherAction(string target, object v)
FlowWatcherAction(string controller_name, string target, object v)
OverloadParameterTypeDescription
DirecttargetstringEntry key to write to when the step finishes
DirectvobjectValue to write
Controllercontroller_namestringLimit this action to the named controller instance
ControllertargetstringEntry key to write to
ControllervobjectValue to write
[FlowStep]
[FlowWatcherAction("room2.BulbOnOff", "Off")] // Sets BulbOnOff = "Off" on completion
[FlowWatcherAction(Room2.BulbOnOff, "On")] // Using token constants
public void Step1()
{
Handler.Next();
}

Model

[Model] marks a class as a flow model. Properties are of type Entry and decorated with [ModelBinding] to bind to Port entries. The platform instantiates the model and passes it as a method parameter to each [FlowStep].

Multiple [ModelBinding] attributes on one property let the same model class serve different controllers — the binding that matches the running controller's name is applied.

Constructors:

// [Model]
Model()
Model(string controllerName)

// [ModelBinding]
ModelBinding(string controller_name, string key)
ModelBinding(string key)
AttributeParameterTypeDescription
[Model]No parameters; marks the class as a Port flow model
[Model]controllerNamestringRestrict this model to a specific controller name
[ModelBinding]controller_namestringController instance name this binding applies to (e.g. "LP1")
[ModelBinding]keystringPort entry key to bind the property to
[ModelBinding] (1-arg)keystringBind to this entry for all controllers (no controller filter)
// Define the model class — outside the controller
[Model]
public class LoadportModel
{
// Bound to LP1_Command when run under "LP1", LP2_Command under "LP2"
[ModelBinding(Controller.LP1, EFEM.LP1_Command)]
[ModelBinding(Controller.LP2, EFEM.LP2_Command)]
public Entry LP_Command { get; set; }

[ModelBinding(Controller.LP1, EFEM.LP1_Configure_Value_i)]
[ModelBinding(Controller.LP2, EFEM.LP2_Configure_Value_i)]
public Entry LP_Configure_Value_i { get; set; }

[ModelBinding(Controller.LP1, EFEM.LP1_Main_Air_i)]
[ModelBinding(Controller.LP2, EFEM.LP2_Main_Air_i)]
public Entry LP_Main_Air_i { get; set; }
}

// Receive model as a parameter in each FlowStep
[FlowStep(0)]
public void StatusCheck(LoadportModel m)
{
Debug.WriteLine(m.LP_Configure_Value_i.Name + " : " + m.LP_Configure_Value_i.Value.String());
Handler.ClearAlarm(-1);
Handler.Next();
}

[FlowStep(1)]
public void SendLoadCommand(LoadportModel m)
{
Debug.WriteLine(m.LP_Main_Air_i.Name + " : " + m.LP_Main_Air_i.Value.String());
Handler.Next();
}

Entry members:

MemberDescription
NameEntry key string (e.g. "EFEM.LP1_Command")
Value.String()Current value as string
Value.Double()Current value as double
Value.Int()Current value as int

Timeout

Sets a timeout for a FlowStep. When the step exceeds the duration, the specified alarm is raised on the given controller.

Constructor:

Timeout(int ms, string controller, int alarmid)
ParameterTypeDescription
msintTimeout duration in milliseconds
controllerstringController instance name this timeout applies to (e.g. "LP1")
alarmidintAlarm ID to raise when the timeout expires
[FlowStep]
[Timeout(0, 0)] // min, max (0 = unlimited)
public void Step1()
{
Handler.Next();
}

Document Attributes

Attributes used for extracting data from Excel/Word documents and generating .page files and C# constant classes.

AttributeTargetInjected TypeArgumentsDescription
[Document]classkeySpecifies source Excel/Word document path
[Save]methodfilenameSpecifies output file paths (.page, .cs)
[ColumnHeader]propertyheaderMaps to Excel column header
[EntryKey]propertyDesignates primary key column
[EntryProperty]propertyDesignates additional property column
[EntryDataType]propertySpecifies SECS data type

Document + Save

Converts an Excel/Word document into a .page file and a C# constants class. This is the approach used in the equipment project to generate device/io.page from IO_Map Ver2.2.docx.

Constructors:

Document(string key)
Save(params string[] filename)
ColumnHeader(string header)
AttributeParameterTypeDescription
[Document]keystringDocument category key or source file path; used to identify the document source
[Save]filenamestring[]One or more output file paths — typically a .page file and a .cs constants file
[ColumnHeader]headerstringExcel column header name to map to this property; omit to match by property name

Step 1: Define a model class that maps Excel columns to .page entry fields.

public class IOModel
{
[ColumnHeader, EntryProperty]
public string Pin_No { set; get; } = string.Empty;

[ColumnHeader, EntryProperty]
public string Port_NO { set; get; } = string.Empty;

[ColumnHeader, EntryKey] // This column becomes the entry Name
public string Description { set; get; } = string.Empty;

[ColumnHeader, EntryProperty]
public string Model { set; get; } = string.Empty;

[ColumnHeader, EntryProperty]
public string Bit_On { set; get; } = string.Empty;

[EntryDataType] // Default data type for all entries
public string DataType { set; get; } = "enum.OffOn";
}
AttributeRole
[ColumnHeader]Maps property to an Excel column by name
[EntryKey]Designates this column as the entry name (first field in .page)
[EntryProperty]Includes this column in the property:{...} JSON
[EntryDataType]Sets the data type field (second field in .page)

Step 2: Define the document converter with [Document] and [Save].

[Document(@"D:\PORT\SampleArduinoLib\equipment\port\IO_Map Ver2.2.docx")]
public class IODocument
{
[Save(@"D:\PORT\SampleArduinoLib\equipment\port\device\io.page",
@"D:\PORT\SampleArduinoLib\equipment\port\io1.cs")]
public Document<IOModel> Convert(Document<IOModel> doc)
{
doc.ForEach(v => v.DataType = "enum.OffOn"); // Set all entries to enum type
doc.ForEach(v => v.Package = "DigitalIO.DI"); // Link all to DigitalIO.DI package
return doc;
}
}

Step 3: Register in your MainWindow to trigger conversion.

Port.Add<IODocument>(@"D:\PORT\SampleArduinoLib\equipment\port\IO_Map Ver2.2.docx");

Generated io.page:

Main_CDA_Pressure_Switch_Check enum.OffOn pkg:DigitalIO.DI property:{"Pin_No":"1","Port_NO":"X01.00","Model":"ISE40A-C6-R-F","Bit_On":"Sensing"}
Door_Lock_On_Check enum.OffOn pkg:DigitalIO.DI property:{"Pin_No":"6","Port_NO":"X01.05","Model":"G7SA-2A2B","Bit_On":"Lock"}
FFU1_Normal_Status enum.OffOn pkg:DigitalIO.DI property:{"Pin_No":"15","Port_NO":"X01.14","Model":"MS-FC300","Bit_On":"Normal"}

Generated io1.cs:

// Auto-generated by Port. Do not edit manually.
namespace equipment
{
public static class Io1
{
public const string Main_CDA_Pressure_Switch_Check = "Main_CDA_Pressure_Switch_Check";
public const string Door_Lock_On_Check = "Door_Lock_On_Check";
public const string FFU1_Normal_Status = "FFU1_Normal_Status";
// ...
}
}

For more detailed usage, see the .NET Integration Guide.