Delete Net462DllTest directory

需要重写
This commit is contained in:
一泓秋水
2025-07-30 12:04:53 +08:00
committed by GitHub
parent 55422d2fbe
commit 93148b11a5
19 changed files with 0 additions and 1878 deletions

View File

@@ -1,12 +0,0 @@
using Serein.Library;
namespace Net462DllTest.Signal
{
/*public enum FromValue
{
[BindValue(typeof(FromWorkBenchView))]
FromWorkBenchView,
[BindValue(typeof(TestFormView))]
TestFormView,
}*/
}

View File

@@ -1,28 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Net462DllTest.Enums
{
public enum PlcState
{
/// <summary>
/// 关机
/// </summary>
PowerOff,
/// <summary>
/// 正在运行
/// </summary>
Runing,
/// <summary>
/// 发生异常
/// </summary>
Error,
/// <summary>
/// 维护中
/// </summary>
Maintenance,
}
}

View File

@@ -1,129 +0,0 @@
using IoTClient.Enums;
using Net462DllTest.Signal;
using static Net462DllTest.Signal.PlcVarInfo;
namespace Net462DllTest.Enums
{
/// <summary>
/// PLC变量信息
/// </summary>
public enum PlcVarName
{
/// <summary>
/// 车位号
/// </summary>
[PlcVarInfo(DataTypeEnum.Int16, "V100", OnNotificationType.OnChanged)]
SpaceNum,
/// <summary>
/// 上位机指令
/// </summary>
[PlcVarInfo(DataTypeEnum.Int16, "V102", OnNotificationType.OnChanged)]
CmdForPLC,
/// <summary>
/// PLC当前存取车位号
/// </summary>
[PlcVarInfo(DataTypeEnum.Int16, "V110", OnNotificationType.OnChanged)]
DoingSpaceNum,
/// <summary>
/// 下位机状态
/// </summary>
[PlcVarInfo(DataTypeEnum.Int16, "V112", OnNotificationType.OnChanged)]
PLCState,
/// <summary>
/// 门1正常待机车位号存车完成地面车位0
/// </summary>
[PlcVarInfo(DataTypeEnum.Int16, "V114", OnNotificationType.OnChanged)]
Door1CurSpaceNum,
/// <summary>
/// 门2正常待机车位号存车完成地面车位0
/// </summary>
[PlcVarInfo(DataTypeEnum.Int16, "V124", OnNotificationType.OnChanged)]
Door2CurSpaceNum,
/// <summary>
/// 下位机运行模式
/// </summary>
[PlcVarInfo(DataTypeEnum.Int16, "V116", OnNotificationType.OnChanged)]
PLCRunMode,
/// <summary>
/// 执行的门号
/// </summary>
[PlcVarInfo(DataTypeEnum.Int16, "V104", OnNotificationType.OnChanged)]
DoorVar,
/// <summary>
/// 门1是否开到位
/// </summary>
[PlcVarInfo(DataTypeEnum.Bool, "V207.0", OnNotificationType.OnChanged)]
IsDoor1OpenDone,
/// <summary>
/// 门1是否关到位
/// </summary>
[PlcVarInfo(DataTypeEnum.Bool, "V207.1", OnNotificationType.OnChanged)]
IsDoor1ClosedDone,
/// <summary>
/// 门2是否开到位
/// </summary>
[PlcVarInfo(DataTypeEnum.Bool, "V207.3", OnNotificationType.OnChanged)]
IsDoor2OpenDone,
/// <summary>
/// 门2是否关到位
/// </summary>
[PlcVarInfo(DataTypeEnum.Bool, "V207.4", OnNotificationType.OnChanged)]
IsDoor2ClosedDone,
/// <summary>
/// 通道1是否有车
/// </summary>
[PlcVarInfo(DataTypeEnum.Bool, "V284.7", OnNotificationType.OnChanged)]
HasCarInTone1,
/// <summary>
/// 通道2是否有车
/// </summary>
[PlcVarInfo(DataTypeEnum.Bool, "V286.7", OnNotificationType.OnChanged)]
HasCarInTone2,
/// <summary>
/// 下位机异常代码
/// </summary>
[PlcVarInfo(DataTypeEnum.Int16, "V2", OnNotificationType.OnChanged)]
ErrorCode,
/// <summary>
/// 2层以上的空板是否在待机
/// </summary>
[PlcVarInfo(DataTypeEnum.Bool, "V200.7", OnNotificationType.OnChanged)]
IsOver2FlowStanded,
/// <summary>
/// 1号门指示灯
/// </summary>
[PlcVarInfo(DataTypeEnum.Bool, "Q17.0", OnNotificationType.OnChanged)]
Gate1Light,
/// <summary>
/// 2号门指示灯
/// </summary>
[PlcVarInfo(DataTypeEnum.Bool, "Q17.3", OnNotificationType.OnChanged)]
Gate2Light,
}
}

View File

@@ -1,52 +0,0 @@
using Net462DllTest.Trigger;
using Serein.Library;
using Serein.Library.Api;
using System;
using System.Threading.Tasks;
namespace Net462DllTest.LogicControl
{
public enum ParkingCommand
{
GetPparkingSpace,
}
[AutoRegister]
[DynamicFlow("[Parking]")]
public class ParkingLogicControl
{
private readonly PrakingDevice PrakingDevice;
public ParkingLogicControl(PrakingDevice PrakingDevice)
{
this.PrakingDevice = PrakingDevice;
}
[NodeAction(NodeType.Flipflop, "等待车位调取命令")]
public async Task<IFlipflopContext<string>> GetPparkingSpace(ParkingCommand parkingCommand = ParkingCommand.GetPparkingSpace)
{
var result = await PrakingDevice.WaitTriggerAsync<string>(parkingCommand);
await Console.Out.WriteLineAsync("收到命令:调取车位,车位号" + result.Value);
return new FlipflopContext<string>(FlipflopStateType.Succeed, result.Value);
}
[NodeAction(NodeType.Action, "调取指定车位")]
public async Task Storage(string spaceNum = "101")
{
if (await PrakingDevice.InvokeTriggerAsync(ParkingCommand.GetPparkingSpace, spaceNum))
{
Console.WriteLine("发送命令成功:调取车位" + spaceNum);
}
else
{
Console.WriteLine("发送命令失败:调取车位" + spaceNum);
}
}
}
}

View File

@@ -1,152 +0,0 @@
using IoTClient.Common.Enums;
using Net462DllTest.Enums;
using Net462DllTest.Model;
using Net462DllTest.Trigger;
using Serein.Library;
using Serein.Library.Api;
using System;
using System.Threading.Tasks;
namespace Net462DllTest.LogicControl
{
[AutoRegister]
[DynamicFlow("[SiemensPlc]")]
public class PlcLogicControl
{
public Guid HandleGuid { get; } = new Guid();
private readonly SiemensPlcDevice MyPlc;
private readonly PlcVarModelDataProxy plcVarModelDataProxy;
public PlcLogicControl(SiemensPlcDevice MyPlc,
PlcVarModelDataProxy plcVarModelDataProxy)
{
this.MyPlc = MyPlc;
this.plcVarModelDataProxy = plcVarModelDataProxy;
}
#region
[NodeAction(NodeType.Loading)] // Loading 初始化完成已注入依赖项,可以开始逻辑上的操作
public void Loading(IFlowContext context)
{
}
[NodeAction(NodeType.Exit)] // 流程结束时自动执行
public void Exit(IFlowContext context)
{
MyPlc.Close();
MyPlc.CancelAllTrigger();
}
#endregion
#region
[NodeAction(NodeType.Flipflop, "等待变量更新")]
public async Task<IFlipflopContext<object>> WaitTask(PlcVarName varName = PlcVarName.ErrorCode)
{
try
{
var triggerData = await MyPlc.WaitTriggerAsync<object>(varName);
await Console.Out.WriteLineAsync($"PLC变量触发器[{varName}]传递数据:{triggerData}");
return new FlipflopContext<object>(FlipflopStateType.Succeed, triggerData);
}
catch (Exception)
{
throw;
}
}
#endregion
#region
[NodeAction(NodeType.Action, "等待")]
public async Task Delay(int ms = 5000)
{
await Console.Out.WriteLineAsync("开始等待");
await Task.Delay(ms);
await Console.Out.WriteLineAsync("不再等待");
}
[NodeAction(NodeType.Action, "PLC初始化")]
public SiemensPlcDevice PlcInit(SiemensVersion version = SiemensVersion.None,
string ip = "192.168.10.100",
int port = 102)
{
//MyPlc.Model.Set(PlcVarName.DoorVar,(Int16)1);
//MyPlc.Model.Get(PlcVarName.DoorVar);
if (MyPlc.Client is null)
{
try
{
MyPlc.Init(version, ip, port);
Console.WriteLine($"西门子PLC初始化成功[{version},{ip}:{port}]");
}
catch (Exception ex)
{
Console.WriteLine($"西门子PLC[{version},{ip}:{port}]初始化异常:{ex.Message}");
}
}
else
{
Console.WriteLine( $"西门子PLC已经初始化[{version},{ip}:{port}]");
}
return MyPlc;
}
[NodeAction(NodeType.Action, "设置PLC状态")]
public SiemensPlcDevice SetState(PlcState state = PlcState.PowerOff)
{
var oldState = MyPlc.State;
MyPlc.State = state;
Console.WriteLine($"PLC状态从[{oldState}]转为[{state}]");
return MyPlc;
}
[NodeAction(NodeType.Action, "PLC获取变量")]
public object ReadVar(PlcVarName varName)
{
var result = MyPlc.Read(varName);
Console.WriteLine($"获取变量成功:({varName})\t result = {result}");
return result;
}
[NodeAction(NodeType.Action, "PLC写入变量")]
public SiemensPlcDevice WriteVar(object value, PlcVarName varName)
{
MyPlc.Write(varName, value); // 新数据
return MyPlc;
}
[NodeAction(NodeType.Action, "批量读取")]
public PlcVarModelDataProxy BatchReadVar()
{
//MyPlc.BatchRefresh();
return plcVarModelDataProxy;
}
[NodeAction(NodeType.Action, "开启定时刷新")]
public void OpenTimedRefresh()
{
Task.Run(async () => await MyPlc.OpenTimedRefreshAsync());
}
[NodeAction(NodeType.Action, "关闭定时刷新")]
public void CloseTimedRefresh()
{
MyPlc.CloseTimedRefresh();
}
#endregion
}
}

View File

@@ -1,46 +0,0 @@
using Serein.Library;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Net462DllTest.Properties
{
/*
理想的项目架构:
每一种功能拆分为新的项目
FlowEnv - LoginControl
LoginControl
↙ ↘
(View-Interaction) (Node-Interaction)
↓ ↕
View ←→ ViewModel ←→ Trigger ← SingleEnum
↓ ↓ ↖
↓ ↓ ↖
Node →→→ Model → Event(OnDataChanged)
视图驱动触发器,触发器驱动数据。
数据驱动触发器,触发器驱动视图。
所以,这个结构≈事件驱动。
动态的配置事件触发的原因、过程与结果。
*/
public class My
{
public void Run()
{
}
}
}

View File

@@ -1,229 +0,0 @@
using Net462DllTest.Enums;
using Serein.Library;
using System;
namespace Net462DllTest.Model
{
/// <summary>
/// PLC变量
/// </summary>
[AutoRegister]
public class PlcVarModel
{
/// <summary>
/// 车位号
/// </summary>
[BindValue(PlcVarName.SpaceNum)]
public Int16 SpaceNum { get; set; }
/// <summary>
/// 上位机指令
/// </summary>
[BindValue(PlcVarName.CmdForPLC)]
public Int16 CmdForPLC { get; set; }
/// <summary>
/// PLC当前存取车位号
/// </summary>
[BindValue(PlcVarName.DoingSpaceNum)]
public Int16 DoingSpaceNum { get; set; }
/// <summary>
/// 下位机状态
/// </summary>
[BindValue(PlcVarName.PLCState)]
public Int16 PLCState { get; set; }
/// <summary>
/// 门1正常待机车位号存车完成地面车位0
/// </summary>
[BindValue(PlcVarName.Door1CurSpaceNum)]
public Int16 Door1CurSpaceNum { get; set; }
/// <summary>
/// 门2正常待机车位号存车完成地面车位0
/// </summary>
[BindValue(PlcVarName.Door2CurSpaceNum)]
public Int16 Door2CurSpaceNum { get; set; }
/// <summary>
/// 下位机运行模式
/// </summary>
[BindValue(PlcVarName.PLCRunMode)]
public Int16 PLCRunMode { get; set; }
/// <summary>
/// 执行的门号
/// </summary>
[BindValue(PlcVarName.DoorVar)]
public Int16 DoorVar { get; set; }
/// <summary>
/// 门1是否开到位
/// </summary>
[BindValue(PlcVarName.IsDoor1OpenDone)]
public bool IsDoor1OpenDone { get; set; }
/// <summary>
/// 门1是否关到位
/// </summary>
[BindValue(PlcVarName.IsDoor1ClosedDone)]
public bool IsDoor1ClosedDone { get; set; }
/// <summary>
/// 门2是否开到位
/// </summary>
[BindValue(PlcVarName.IsDoor2OpenDone)]
public bool IsDoor2OpenDone { get; set; }
/// <summary>
/// 门2是否关到位
/// </summary>
[BindValue(PlcVarName.IsDoor2ClosedDone)]
public bool IsDoor2ClosedDone { get; set; }
/// <summary>
/// 通道1是否有车
/// </summary>
[BindValue(PlcVarName.HasCarInTone1)]
public bool HasCarInTone1 { get; set; }
/// <summary>
/// 通道2是否有车
/// </summary>
[BindValue(PlcVarName.HasCarInTone2)]
public bool HasCarInTone2 { get; set; }
/// <summary>
/// 下位机异常代码
/// </summary>
[BindValue(PlcVarName.ErrorCode)]
public Int16 ErrorCode { get; set; }
/// <summary>
/// 2层以上的空板是否在待机
/// </summary>
[BindValue(PlcVarName.IsOver2FlowStanded)]
public bool IsOver2FlowStanded { get; set; }
/// <summary>
/// 1号门指示灯
/// </summary>
[BindValue(PlcVarName.Gate1Light)]
public bool Gate1Light { get; set; }
/// <summary>
/// 2号门指示灯
/// </summary>
[BindValue(PlcVarName.Gate2Light)]
public bool Gate2Light { get; set; }
}
/// <summary>
/// 数据代理防止View修改
/// </summary>
[AutoRegister]
public class PlcVarModelDataProxy
{
private readonly PlcVarModel plcVarModel;
public PlcVarModelDataProxy(PlcVarModel plcVarModel)
{
this.plcVarModel = plcVarModel;
}
/// <summary>
/// 车位号
/// </summary>
public Int16 SpaceNum { get => plcVarModel.SpaceNum; }
/// <summary>
/// 上位机指令
/// </summary>
public Int16 CmdForPLC { get => plcVarModel.CmdForPLC; }
/// <summary>
/// PLC当前存取车位号
/// </summary>
public Int16 DoingSpaceNum { get => plcVarModel.DoingSpaceNum; }
/// <summary>
/// 下位机状态
/// </summary>
public Int16 PLCState { get => plcVarModel.PLCState; }
/// <summary>
/// 门1正常待机车位号存车完成地面车位0
/// </summary>
public Int16 Door1CurSpaceNum { get => plcVarModel.Door1CurSpaceNum; }
/// <summary>
/// 门2正常待机车位号存车完成地面车位0
/// </summary>
public Int16 Door2CurSpaceNum { get => plcVarModel.Door2CurSpaceNum; }
/// <summary>
/// 下位机运行模式
/// </summary>
public Int16 PLCRunMode { get => plcVarModel.PLCRunMode; }
/// <summary>
/// 执行的门号
/// </summary>
public Int16 DoorVar { get => plcVarModel.DoorVar; }
/// <summary>
/// 门1是否开到位
/// </summary>
public bool IsDoor1OpenDone { get => plcVarModel.IsDoor1OpenDone; }
/// <summary>
/// 门1是否关到位
/// </summary>
public bool IsDoor1ClosedDone { get => plcVarModel.IsDoor1ClosedDone; }
/// <summary>
/// 门2是否开到位
/// </summary>
public bool IsDoor2OpenDone { get => plcVarModel.IsDoor2OpenDone; }
/// <summary>
/// 门2是否关到位
/// </summary>
public bool IsDoor2ClosedDone { get => plcVarModel.IsDoor2ClosedDone; }
/// <summary>
/// 通道1是否有车
/// </summary>
public bool HasCarInTone1 { get => plcVarModel.HasCarInTone1; }
/// <summary>
/// 通道2是否有车
/// </summary>
public bool HasCarInTone2 { get => plcVarModel.HasCarInTone2; }
/// <summary>
/// 下位机异常代码
/// </summary>
public Int16 ErrorCode { get => plcVarModel.ErrorCode; }
/// <summary>
/// 2层以上的空板是否在待机
/// </summary>
public bool IsOver2FlowStanded { get => plcVarModel.IsOver2FlowStanded; }
/// <summary>
/// 1号门指示灯
/// </summary>
public bool Gate1Light { get => plcVarModel.Gate1Light; }
/// <summary>
/// 2号门指示灯
/// </summary>
public bool Gate2Light { get => plcVarModel.Gate2Light; }
}
}

View File

@@ -1,98 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E40EE629-1A38-4011-88E3-9AD036869987}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>Net462DllTest</RootNamespace>
<AssemblyName>Net462DllTest</AssemblyName>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="IoTClient, Version=1.0.40.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\IoTClient.1.0.40\lib\netstandard2.0\IoTClient.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Ports, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Ports.4.6.0\lib\net461\System.IO.Ports.dll</HintPath>
</Reference>
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="Model\PlcVarModel.cs" />
<Compile Include="Main.cs" />
<Compile Include="Trigger\SiemensPlcDevice.cs" />
<Compile Include="Trigger\PrakingDevice.cs" />
<Compile Include="Enums\PlcState.cs" />
<Compile Include="Enums\PlcVarName.cs" />
<Compile Include="LogicControl\PlcLogicControl.cs" />
<Compile Include="LogicControl\ParkingLogicControl.cs" />
<Compile Include="Enums\FromValue.cs" />
<Compile Include="Signal\CommandSignal.cs" />
<Compile Include="Signal\PLCVarSignal.cs" />
<Compile Include="Trigger\ViewManagement.cs" />
<Compile Include="Utils\GSModel.cs" />
<Compile Include="Utils\RelayCommand.cs" />
<Compile Include="Web\FlowController.cs" />
<Compile Include="Web\PlcSocketService.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Library\Serein.Library.csproj">
<Project>{5e19d0f2-913a-4d1c-a6f8-1e1227baa0e3}</Project>
<Name>Serein.Library</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -1,15 +0,0 @@
namespace Net462DllTest.Signal
{
public enum CommandSignal
{
Command_1,
Command_2,
Command_3,
Command_4,
Command_5,
Command_6,
Command_7,
Command_8,
Command_9,
}
}

View File

@@ -1,96 +0,0 @@
using IoTClient.Enums;
using Net462DllTest.Enums;
using System;
using static Net462DllTest.Signal.PlcVarInfo;
namespace Net462DllTest.Signal
{
[AttributeUsage(AttributeTargets.Field)]
public class PlcVarInfoAttribute : Attribute
{
public PlcVarInfo Info { get; }
/// <summary>
///
/// </summary>
/// <param name="dateType">数据类型</param>
/// <param name="address">变量地址</param>
/// <param name="notificationType">通知行为类型</param>
/// <param name="isReadOnly">是否只读</param>
/// <param name="isTimingRead">是否定时刷新</param>
/// <param name="interval">刷新间隔ms</param>
public PlcVarInfoAttribute(DataTypeEnum dateType,
string address,
OnNotificationType notificationType,
bool isReadOnly = false,
bool isTimingRead = true,
int interval = 1000
)
{
Info = new PlcVarInfo()
{
DataType = dateType,
IsReadOnly = isReadOnly,
Address = address,
Interval = interval,
IsTimingRead= isTimingRead,
NotificationType = notificationType,
};
}
}
public class PlcVarInfo
{
public enum OnNotificationType
{
/// <summary>
/// 刷新时通知每次写入Model后都会触发相应的触发器
/// </summary>
OnRefresh,
/// <summary>
/// 改变时才通知与Model对应数据不一致时才会触发相应的触发器
/// </summary>
OnChanged,
}
/// <summary>
/// 变量类型
/// </summary>
public PlcVarName Name { get; set; }
/// <summary>
/// 数据类型
/// </summary>
public DataTypeEnum DataType { get; set; }
/// <summary>
/// 变量地址
/// </summary>
public string Address { get; set; }
/// <summary>
/// 变量是否只读
/// </summary>
public bool IsReadOnly { get; set; }
/// <summary>
/// 是否定时刷新
/// </summary>
public bool IsTimingRead { get; set; }
/// <summary>
/// 刷新间隔ms
/// </summary>
public int Interval { get; set; } = 100; // 100ms
public OnNotificationType NotificationType { get; set; } = OnNotificationType.OnChanged;
public override string ToString()
{
if (IsTimingRead)
{
return $"数据:{Name},类型:{DataType},地址:{Address},只读:{IsReadOnly},自动刷新:{IsTimingRead},刷新间隔:{Interval}";
}
else
{
return $"数据:{Name},类型:{DataType},地址:{Address},只读:{IsReadOnly}";
}
}
}
}

View File

@@ -1,12 +0,0 @@
using Net462DllTest.LogicControl;
using Serein.Library;
using Serein.Library.Utils;
namespace Net462DllTest.Trigger
{
[AutoRegister]
public class PrakingDevice : TaskFlowTrigger<ParkingCommand>
{
}
}

View File

@@ -1,463 +0,0 @@
using IoTClient;
using IoTClient.Clients.PLC;
using IoTClient.Common.Enums;
using IoTClient.Enums;
using Net462DllTest.Enums;
using Net462DllTest.Model;
using Net462DllTest.Signal;
using Net462DllTest.Utils;
using Serein.Library;
using Serein.Library.Utils;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
namespace Net462DllTest.Trigger
{
[AutoRegister]
public class SiemensPlcDevice : TaskFlowTrigger<PlcVarName>
{
public SiemensClient Client { get; set; }
public SiemensVersion Version { get; set; }
public string IP { get; set; } = "127.0.0.1";
public int Port { get; set; } = 102;
public PlcState State { get; set; } = PlcState.PowerOff;
public bool IsTimedRefresh { get; set; } = false; // 是否定时刷新
public IGSModel<PlcVarName, PlcVarModel> Model { get; }
private readonly object _lockObj = new object(); // 防止多次初始化读取任务
private readonly ConcurrentBag<Task> TimedRefreshTask = new ConcurrentBag<Task>(); // 定时读取任务
private readonly ConcurrentBag<PlcVarInfo> VarInfos = new ConcurrentBag<PlcVarInfo>(); // 所有变量信息
private readonly ConcurrentBag<PlcVarInfo> OnRefreshs = new ConcurrentBag<PlcVarInfo>(); // 数据变更后需要通知触发器的变量信息
private readonly ConcurrentBag<PlcVarInfo> OnChangeds = new ConcurrentBag<PlcVarInfo>(); // 读取读取后需要通知触发器的变量信息
public SiemensPlcDevice(PlcVarModel model)
{
this.Model = new GSModel<PlcVarName, PlcVarModel>(model);
LoadVarInfos();
}
/// <summary>
/// 加载变量信息
/// </summary>
private void LoadVarInfos()
{
foreach (var property in typeof(PlcVarModel).GetProperties())
{
var attribute = property.GetCustomAttribute<BindValueAttribute>();
if (attribute?.Value is PlcVarName varName)
{
var varInfo = varName.ToVarInfo();
VarInfos.Add(varInfo);
if (varInfo.IsTimingRead) // 是否定时刷新
{
switch (varInfo.NotificationType)
{
case PlcVarInfo.OnNotificationType.OnRefresh:
OnRefreshs.Add(varInfo); // 变量读取时通知触发器的变量
break;
case PlcVarInfo.OnNotificationType.OnChanged:
OnChangeds.Add(varInfo); // 变量变更时才需要通知触发器的变量
break;
}
}
}
}
}
public void Init(SiemensVersion version, string ip, int port)
{
Client = new SiemensClient(version, ip, port);
Version = version;
IP = ip;
Port = port;
Client.Open();
}
public void Close()
{
Client?.Close();
Client = null;
}
public void Write(PlcVarName varName, object value)
{
var varInfo = varName.ToVarInfo();
if (this.State == PlcState.Runing)
{
if (varInfo.IsReadOnly)
{
throw new Exception($"PLC变量{varInfo}当前禁止写入");
}
else
{
Client.WriteVar(varInfo, value); // 尝试写入PLC
Model.Set(varName, value);
Console.WriteLine($"PLC变量{varInfo}写入数据:{value}");
}
}
else
{
throw new Exception($"PLC处于非预期状态{this.State}");
}
}
public object Read(PlcVarName varName)
{
var varInfo = varName.ToVarInfo();
var result = Client.ReadVar(varInfo);// 尝试读取数据
Model.Set(varName, result); // 缓存读取的数据
return result;
}
public void BatchRefresh()
{
foreach(var varInfo in VarInfos)
{
Read(varInfo.Name); // 无条件批量读取
}
}
/// <summary>
/// 开启定时批量读取任务
/// </summary>
/// <returns></returns>
public async Task OpenTimedRefreshAsync()
{
if (TimedRefreshTask.IsEmpty)
{
InitTimedRefreshTask();
}
IsTimedRefresh = true;
await Task.WhenAll(TimedRefreshTask.ToArray());
}
/// <summary>
/// 关闭定时任务
/// </summary>
public void CloseTimedRefresh() => IsTimedRefresh = false;
/// <summary>
/// 初始化定时刷新任务
/// </summary>
public void InitTimedRefreshTask()
{
Console.WriteLine("开始初始化刷新任务");
lock (_lockObj)
{
foreach (var varInfo in OnChangeds)
{
Console.WriteLine($"添加监视任务(OnChanged){varInfo.Name}");
ScheduleTask(varInfo);
}
foreach (var varInfo in OnRefreshs)
{
Console.WriteLine($"添加监视任务(OnRefresh){varInfo.Name}");
ScheduleTask(varInfo);
}
}
}
/// <summary>
/// 定时读取PLC变量并刷新Model的值
/// </summary>
/// <param name="varInfo"></param>
private void ScheduleTask(PlcVarInfo varInfo)
{
var task = Task.Run(async () =>
{
var signal = varInfo.Name;
object oldData;
object newData;
bool isNotification;
while (true)
{
await Task.Delay(varInfo.Interval);
if (!IsTimedRefresh || Client is null) break;
oldData = Model.Get(signal); // 暂存旧数据
newData = Read(signal); // 获取新数据
if (varInfo.NotificationType == PlcVarInfo.OnNotificationType.OnRefresh)
{
isNotification = true; // 无条件触发通知
}
else
{
isNotification = !oldData.Equals(newData); // 变更时才会触发通知
}
if (isNotification)
{
Console.WriteLine($"VarName: {signal}\t\tOld Data: {oldData}\tNew Data: {newData}");
await InvokeTriggerAsync(signal, newData);
}
}
});
TimedRefreshTask.Add(task);
}
public override string ToString()
{
return $"西门子Plc[{this.Version}-{this.IP}:{this.Port}]";
}
}
/// <summary>
/// 拓展方法
/// </summary>
public static class MyPlcExtension
{
/// <summary>
/// 缓存变量信息
/// </summary>
private static readonly Dictionary<PlcVarName, PlcVarInfo> VarInfoDict = new Dictionary<PlcVarName, PlcVarInfo>();
/// <summary>
/// 获取变量信息
/// </summary>
/// <param name="plcVarEnum"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static PlcVarInfo ToVarInfo(this PlcVarName plcVarEnum)
{
if (VarInfoDict.ContainsKey(plcVarEnum))
{
return VarInfoDict[plcVarEnum];
}
var plcValue = EnumHelper.GetAttributeValue<PlcVarName, PlcVarInfoAttribute, PlcVarInfo>(plcVarEnum, attr => attr.Info)
?? throw new Exception($"获取变量异常:{plcVarEnum}没有标记PlcValueAttribute");
if (string.IsNullOrEmpty(plcValue.Address))
{
throw new Exception($"获取变量异常:{plcVarEnum},变量地址为空");
}
VarInfoDict.Add(plcVarEnum, plcValue);
plcValue.Name = plcVarEnum;
return plcValue;
}
/// <summary>
/// 读取PLC
/// </summary>
public static object ReadVar(this SiemensClient client, PlcVarInfo varInfo)
{
if (client is null)
{
throw new ArgumentNullException($"PLC尚未初始化");
}
object resultvalue;
switch (varInfo.DataType)
{
case DataTypeEnum.String:
var resultString = client.ReadString(varInfo.Address);
if (!resultString.IsSucceed) throw new Exception(resultString.Err);
resultvalue = resultString.Value;
break;
case DataTypeEnum.Bool:
var resultBool = client.ReadBoolean(varInfo.Address);
if (!resultBool.IsSucceed) throw new Exception(resultBool.Err);
resultvalue = resultBool.Value;
break;
case DataTypeEnum.Float:
var resultFloat = client.ReadFloat(varInfo.Address);
if (!resultFloat.IsSucceed) throw new Exception(resultFloat.Err);
resultvalue = resultFloat.Value;
break;
case DataTypeEnum.Double:
var resultDouble = client.ReadDouble(varInfo.Address);
if (!resultDouble.IsSucceed) throw new Exception(resultDouble.Err);
resultvalue = resultDouble.Value;
break;
case DataTypeEnum.Byte:
var resultByte = client.ReadByte(varInfo.Address);
if (!resultByte.IsSucceed) throw new Exception(resultByte.Err);
resultvalue = resultByte.Value;
break;
case DataTypeEnum.Int16:
var resultInt16 = client.ReadInt16(varInfo.Address);
if (!resultInt16.IsSucceed) throw new Exception(resultInt16.Err);
resultvalue = resultInt16.Value;
break;
case DataTypeEnum.UInt16:
var resultUint16 = client.ReadUInt16(varInfo.Address);
if (!resultUint16.IsSucceed) throw new Exception(resultUint16.Err);
resultvalue = resultUint16.Value;
break;
case DataTypeEnum.Int32:
var resultInt32 = client.ReadInt32(varInfo.Address);
if (!resultInt32.IsSucceed) throw new Exception(resultInt32.Err);
resultvalue = resultInt32.Value;
break;
case DataTypeEnum.UInt32:
var resultUInt32 = client.ReadUInt32(varInfo.Address);
if (!resultUInt32.IsSucceed) throw new Exception(resultUInt32.Err);
resultvalue = resultUInt32.Value;
break;
case DataTypeEnum.Int64:
var resultInt64 = client.ReadInt64(varInfo.Address);
if (!resultInt64.IsSucceed) throw new Exception(resultInt64.Err);
resultvalue = resultInt64.Value;
break;
case DataTypeEnum.UInt64:
var resultUInt64 = client.ReadUInt64(varInfo.Address);
if (!resultUInt64.IsSucceed) throw new Exception(resultUInt64.Err);
resultvalue = resultUInt64.Value;
break;
default:
throw new NotImplementedException($"变量为指定数据类型,或是非预期的数据类型{varInfo}");
}
return resultvalue;
}
/// <summary>
/// 转换数据类型写入PLC
/// </summary>
public static object WriteVar(this SiemensClient client, PlcVarInfo varInfo, object value)
{
if (client is null)
{
throw new ArgumentNullException($"PLC尚未初始化");
}
DataTypeEnum dataType = varInfo.DataType;
object convertValue;
Result result;
switch (dataType)
{
case DataTypeEnum.String:
var @string = value.ToString();
convertValue = @string;
result = client.Write(varInfo.Address, @string);
break;
case DataTypeEnum.Bool:
var @bool = bool.Parse(value.ToString());
convertValue = @bool;
result = client.Write(varInfo.Address, @bool);
break;
case DataTypeEnum.Float:
var @float = float.Parse(value.ToString());
convertValue = @float;
result = client.Write(varInfo.Address, @float);
break;
case DataTypeEnum.Double:
var @double = double.Parse(value.ToString());
convertValue = @double;
result = client.Write(varInfo.Address, @double);
break;
case DataTypeEnum.Byte:
var @byte = byte.Parse(value.ToString());
convertValue = @byte;
result = client.Write(varInfo.Address, @byte);
break;
case DataTypeEnum.Int16:
var @short = short.Parse(value.ToString());
convertValue = @short;
result = client.Write(varInfo.Address, @short);
break;
case DataTypeEnum.UInt16:
var @ushort = ushort.Parse(value.ToString());
convertValue = @ushort;
result = client.Write(varInfo.Address, @ushort);
break;
case DataTypeEnum.Int32:
var @int = int.Parse(value.ToString());
convertValue = @int;
result = client.Write(varInfo.Address, @int);
break;
case DataTypeEnum.UInt32:
var @uint = uint.Parse(value.ToString());
convertValue = @uint;
result = client.Write(varInfo.Address, @uint);
break;
case DataTypeEnum.Int64:
var @long = long.Parse(value.ToString());
convertValue = @long;
result = client.Write(varInfo.Address, @long);
break;
case DataTypeEnum.UInt64:
var @ulong = ulong.Parse(value.ToString());
convertValue = @ulong;
result = client.Write(varInfo.Address, @ulong);
break;
default:
throw new NotImplementedException($"变量为指定数据类型,或是非预期的数据类型{varInfo}");
}
if (result.IsSucceed)
{
return convertValue;
}
else
{
throw new Exception(result.Err);
}
}
/// <summary>
/// 类型转换
/// </summary>
/// <param name="dataType"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
//public static Type ToDataType(this DataTypeEnum dataType)
//{
// Type plcDataType;
// switch (dataType)
// {
// case DataTypeEnum.String:
// plcDataType = typeof(string);
// break;
// case DataTypeEnum.Bool:
// plcDataType = typeof(bool);
// break;
// case DataTypeEnum.Float:
// plcDataType = typeof(float);
// break;
// case DataTypeEnum.Double:
// plcDataType = typeof(double);
// break;
// case DataTypeEnum.Byte:
// plcDataType = typeof(byte);
// break;
// case DataTypeEnum.Int16:
// plcDataType = typeof(short);
// break;
// case DataTypeEnum.UInt16:
// plcDataType = typeof(ushort);
// break;
// case DataTypeEnum.Int32:
// plcDataType = typeof(int);
// break;
// case DataTypeEnum.UInt32:
// plcDataType = typeof(uint);
// break;
// case DataTypeEnum.Int64:
// plcDataType = typeof(long);
// break;
// case DataTypeEnum.UInt64:
// plcDataType = typeof(ulong);
// break;
// default:
// throw new NotImplementedException();
// }
// return plcDataType;
//}
}
}

View File

@@ -1,84 +0,0 @@
using Net462DllTest.Signal;
using Serein.Library;
using Serein.Library.Api;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
using System.Windows.Forms;
using System.Windows.Threading;
using Serein.Library.Utils;
namespace Net462DllTest.Trigger
{
/// <summary>
/// 视图管理
/// </summary>
[AutoRegister]
public class ViewManagement : TaskFlowTrigger<CommandSignal>
{
private readonly UIContextOperation uiContextOperation;
public ViewManagement(UIContextOperation uiContextOperation)
{
this.uiContextOperation = uiContextOperation;
}
public int Id = new Random().Next(1, 10000);
private readonly List<Form> forms = new List<Form>();
/// <summary>
/// 打开窗口
/// </summary>
/// <param name="form">要打开的窗口类型</param>
/// <param name="isTop">是否置顶</param>
public void OpenView(Form form, bool isTop)
{
//Application.Current.Dispatcher.
forms.Add(form);
uiContextOperation.Invoke(() => {
form.TopMost = isTop;
form.Show();
});
//environment.IOC.Run<SynchronizationContext>(uiContext =>
//{
// uiContext?.Post(state => {
// },null);
//});
//var uiContext = SynchronizationContext.Current;
//Task.Run(() =>
//{
// uiContext.Post(_ =>
// {
// }, null);
//});
}
public void CloseView(Type formType)
{
var remoteForms = forms.Where(f => f.GetType() == formType).ToArray();
Dispatcher.CurrentDispatcher.Invoke(() =>
{
foreach (Form f in remoteForms)
{
f.Close();
f.Dispose();
this.forms.Remove(f);
}
});
}
}
}

View File

@@ -1,157 +0,0 @@
using Serein.Library;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
namespace Net462DllTest.Utils
{
public interface IGSModel<TKey, TModel>
{
//TModel Value { get; set; }
void Set(TKey tEnum, object value);
object Get(TKey tEnum);
}
/// <summary>
/// 通过 Emit 创建 set/get 委托
/// </summary>
public class GSModel<TKey, TModel> : IGSModel<TKey, TModel>
where TKey : struct, Enum
where TModel : class
{
private TModel Value;
public GSModel(TModel Model)
{
this.Value = Model;
}
// 缓存创建好的setter和getter委托
private readonly Dictionary<TKey, Action<TModel, object>> _setterCache = new Dictionary<TKey, Action<TModel, object>>();
private readonly Dictionary<TKey, Func<TModel, object>> _getterCache = new Dictionary<TKey, Func<TModel, object>>();
public void Set(TKey tEnum, object value)
{
if (!_setterCache.TryGetValue(tEnum, out var setter))
{
PropertyInfo property = GetPropertyByEnum(tEnum);
if (property == null)
{
_setterCache[tEnum] = (s, o) => throw new ArgumentException($"没有对应的Model属性{{{tEnum}");
}
else
{
// 创建并缓存setter委托
setter = CreateSetter(property);
_setterCache[tEnum] = setter;
}
}
// 使用缓存的setter委托设置值
setter(Value, value);
}
public object Get(TKey tEnum)
{
if (!_getterCache.TryGetValue(tEnum, out var getter))
{
PropertyInfo property = GetPropertyByEnum(tEnum);
if (property == null)
{
_setterCache[tEnum] = (s, o) => throw new ArgumentException($"没有对应的Model属性{tEnum}");
}
else
{
// 创建并缓存getter委托
getter = CreateGetter(property);
_getterCache[tEnum] = getter;
}
}
// 使用缓存的getter委托获取值
return getter(Value);
}
private PropertyInfo GetPropertyByEnum(TKey tEnum)
{
foreach (var property in typeof(TModel).GetProperties())
{
var attribute = property.GetCustomAttribute<BindValueAttribute>();
if (attribute?.Value?.GetType()?.IsEnum == true)
{
if (attribute.Value is TKey @enum && @enum.Equals(tEnum))
{
return property;
}
}
}
return null;
}
// 动态创建调用Setter方法
private Action<TModel, object> CreateSetter(PropertyInfo property)
{
var method = new DynamicMethod("Set" + property.Name, null, new[] { typeof(TModel), typeof(object) }, true);
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // 加载实例PlcVarValue
il.Emit(OpCodes.Ldarg_1); // 加载值object
if (property.PropertyType.IsValueType)
{
il.Emit(OpCodes.Unbox_Any, property.PropertyType); // 解箱并转换为值类型
}
else
{
il.Emit(OpCodes.Castclass, property.PropertyType); // 引用类型转换
}
il.Emit(OpCodes.Callvirt, property.GetSetMethod()); // 调用属性的Setter方法
il.Emit(OpCodes.Ret); // 返回
return (Action<TModel, object>)method.CreateDelegate(typeof(Action<TModel, object>));
}
/// <summary>
/// 动态创建调用Getter方法
/// </summary>
/// <param name="property"></param>
/// <returns></returns>
private Func<TModel, object> CreateGetter(PropertyInfo property)
{
var method = new DynamicMethod("Get" + property.Name, typeof(object), new[] { typeof(TModel) }, true);
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // 加载实例PlcVarValue
il.Emit(OpCodes.Callvirt, property.GetGetMethod()); // 调用属性的Getter方法
if (property.PropertyType.IsValueType)
{
il.Emit(OpCodes.Box, property.PropertyType); // 值类型需要装箱
}
il.Emit(OpCodes.Ret); // 返回
return (Func<TModel, object>)method.CreateDelegate(typeof(Func<TModel, object>));
}
}
}

View File

@@ -1,41 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Net462DllTest.Utils
{
/// <summary>
/// 用于窗体View与ViewModel进行交互
/// </summary>
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object,bool> _canExecute;
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter = null)
{
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter = null)
{
_execute(parameter);
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
}

View File

@@ -1,80 +0,0 @@
using Net462DllTest.Enums;
using Net462DllTest.Signal;
using Net462DllTest.Trigger;
using Serein.Library;
using Serein.Library.Utils;
using Serein.Library.Web;
using System;
namespace Net462DllTest.Web
{
[WebApiController]
public class FlowController : ControllerBase
{
private readonly SiemensPlcDevice plcDevice;
private readonly ViewManagement viewManagement;
public FlowController(SiemensPlcDevice plcDevice, ViewManagement viewManagement)
{
this.plcDevice = plcDevice;
this.viewManagement = viewManagement;
}
/*
* 类型 POST
* url : http://127.0.0.1:8089/flow/plcop?var=
* url : http://127.0.0.1:8089/flow/plcop?var=SpaceNum
* body [JSON]
*
* {
* "value":0,
* }
*/
[WebApi(ApiType.POST)]
public dynamic PlcOp([Url] string var, int value)
{
if (EnumHelper.TryConvertEnum<PlcVarName>(var, out var signal))
{
SereinEnv.WriteLine(InfoType.INFO, $"外部触发 {signal} 信号,信号内容 {value} ");
_ = plcDevice.InvokeTriggerAsync(signal, value);// 通过 Web Api 模拟外部输入信号
return new { state = "succeed" };
}
else
{
return new { state = "fail" };
}
}
/*
* 类型 POST
* url : http://127.0.0.1:8089/flow/trigger?command=
* url : http://127.0.0.1:8089/flow/trigger?command=Command_1
* body [JSON]
*
* {
* "value":0,
* }
*/
[WebApi(ApiType.POST)]
public dynamic Trigger([Url] string command, int value)
{
if (EnumHelper.TryConvertEnum<CommandSignal>(command, out var signal))
{
SereinEnv.WriteLine(InfoType.INFO, $"外部触发 {signal} 信号,信号内容 {value} ");
_ = viewManagement.InvokeTriggerAsync(signal, value);// 通过 Web Api 模拟外部输入信号
return new { state = "succeed" };
}
else
{
return new { state = "fail" };
}
}
}
}

View File

@@ -1,166 +0,0 @@
using IoTClient.Common.Enums;
using Net462DllTest.Enums;
using Net462DllTest.Model;
using Net462DllTest.Trigger;
using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Network.WebSocketCommunication;
using Serein.Library.Web;
using System;
using System.Threading.Tasks;
namespace Net462DllTest.Web
{
[AutoRegister]
[DynamicFlow("[PlcSocketService]")]
[AutoSocketModule(ThemeKey = "theme", DataKey = "data")]
public class PlcSocketService : ISocketHandleModule
{
public Guid HandleGuid { get; } = new Guid();
private readonly SiemensPlcDevice MyPlc;
private readonly PlcVarModelDataProxy plcVarModelDataProxy;
public PlcSocketService(SiemensPlcDevice MyPlc,
PlcVarModelDataProxy plcVarModelDataProxy)
{
this.MyPlc = MyPlc;
this.plcVarModelDataProxy = plcVarModelDataProxy;
}
#region 退
[NodeAction(NodeType.Init)]
public void Init(IFlowContext context)
{
var ioc = context.Env.FlowControl.IOC;
ioc.Register<WebSocketServer>();
ioc.Register<WebSocketClient>();
ioc.Register<IRouter, Router>();
ioc.Register<WebApiServer>();
}
[NodeAction(NodeType.Loading)] // Loading 初始化完成已注入依赖项,可以开始逻辑上的操作
public void Loading(IFlowContext context)
{
var ioc = context.Env.FlowControl.IOC;
// 注册控制器
ioc.Run<IRouter, WebApiServer>((router, apiServer) => {
router.AddHandle(typeof(FlowController));
apiServer.Start("http://*:8089/"); // 开启 Web Api 服务
});
ioc.Run<WebSocketServer>(async (socketServer) => {
socketServer.MsgHandleHelper.AddModule(this, (ex, recover) =>
{
recover(new
{
ex = ex.Message,
storehouseInfo = ex.StackTrace
});
});
await socketServer.StartAsync("http://localhost:5005/"); // 开启 Web Socket 监听
});
ioc.Run<WebSocketClient>(async client => {
await client.ConnectAsync("ws://localhost:5005/"); // 连接到服务器
});
}
[NodeAction(NodeType.Exit)] // 流程结束时自动执行
public void Exit(IFlowContext context)
{
var ioc = context.Env.FlowControl.IOC;
ioc.Run<WebApiServer>((apiServer) =>
{
apiServer?.Stop(); // 关闭 Web 服务
});
ioc.Run<WebSocketServer>((socketServer) =>
{
socketServer.MsgHandleHelper.RemoveModule(this);
socketServer?.Stop(); // 关闭 Web 服务
});
MyPlc.Close();
MyPlc.CancelAllTrigger();
}
#endregion
[AutoSocketHandle]
public async Task BatchReadVar(Func<string, Task> SendMsg, Func<object, Task> SendObj)
{
await SendMsg("开始刷新数据");
//MyPlc.BatchRefresh();
await Task.Delay(1000);
await SendMsg("刷新完成");
await SendObj(plcVarModelDataProxy);
await SendMsg("发送完成");
}
[AutoSocketHandle]
public object ReadVar(PlcVarName varName)
{
var result = MyPlc.Read(varName);
Console.WriteLine($"获取变量成功:({varName})\t result = {result}");
return result;
}
[AutoSocketHandle(IsReturnValue = false)]
public SiemensPlcDevice WriteVar(object value, PlcVarName varName)
{
MyPlc.Write(varName, value); // 新数据
return MyPlc;
}
[AutoSocketHandle(IsReturnValue = false)]
public SiemensPlcDevice PlcInit(SiemensVersion version = SiemensVersion.None,
string ip = "192.168.10.100",
int port = 102)
{
if (MyPlc.Client is null)
{
try
{
MyPlc.Init(version, ip, port);
Console.WriteLine($"西门子PLC初始化成功[{version},{ip}:{port}]");
}
catch (Exception ex)
{
Console.WriteLine($"西门子PLC[{version},{ip}:{port}]初始化异常:{ex.Message}");
}
}
else
{
Console.WriteLine($"西门子PLC已经初始化[{version},{ip}:{port}]");
}
return MyPlc;
}
[AutoSocketHandle(IsReturnValue = false)]
public SiemensPlcDevice SetState(PlcState state = PlcState.PowerOff)
{
var oldState = MyPlc.State;
MyPlc.State = state;
Console.WriteLine($"PLC状态从[{oldState}]转为[{state}]");
return MyPlc;
}
public void OpenTimedRefresh()
{
Task.Run(async () => await MyPlc.OpenTimedRefreshAsync());
}
public void CloseTimedRefresh()
{
MyPlc.CloseTimedRefresh();
}
}
}

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.IO.Ports" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" /></startup></configuration>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="IoTClient" version="1.0.40" targetFramework="net461" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net462" />
<package id="System.IO.Ports" version="4.6.0" targetFramework="net461" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net461" />
</packages>