lixun há 2 anos atrás
pai
commit
bcf8db4a27
60 ficheiros alterados com 648 adições e 25 exclusões
  1. 2 1
      readme.txt
  2. 1 1
      src/YSAI.DAQ/YSAI.Core/interface/only/IS7.cs
  3. 44 0
      src/YSAI.DAQ/YSAI.Modbus/service/Common/ServerSocketBase.cs
  4. 7 7
      src/YSAI.DAQ/YSAI.S7/S7Data.cs
  5. 10 10
      src/YSAI.DAQ/YSAI.S7/S7Operate.cs
  6. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/COTP.cs
  7. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Compat/TcpClientMixins.cs
  8. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Conversion.cs
  9. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Enums.cs
  10. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Helper/MemoryStreamExtension.cs
  11. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Internal/TaskQueue.cs
  12. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/InvalidDataException.cs
  13. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/PLC.cs
  14. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/PLCAddress.cs
  15. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/PLCExceptions.cs
  16. 3 2
      src/YSAI.DAQ/YSAI.S7/lib/S7.Net/PLCHelpers.cs
  17. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/PlcAsynchronous.cs
  18. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/PlcException.cs
  19. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/PlcSynchronous.cs
  20. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Protocol/ConnectionRequest.cs
  21. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Protocol/ReadWriteErrorCode.cs
  22. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Protocol/S7/DataItemAddress.cs
  23. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Protocol/S7WriteMultiple.cs
  24. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Protocol/Serialization.cs
  25. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Protocol/Tsap.cs
  26. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Protocol/TsapPair.cs
  27. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/StreamExtensions.cs
  28. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/TPKT.cs
  29. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Bit.cs
  30. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Boolean.cs
  31. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Byte.cs
  32. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/ByteArray.cs
  33. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Class.cs
  34. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Counter.cs
  35. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/DInt.cs
  36. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/DWord.cs
  37. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/DataItem.cs
  38. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/DateTime.cs
  39. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/DateTimeLong.cs
  40. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Double.cs
  41. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Int.cs
  42. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/LReal.cs
  43. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Real.cs
  44. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/S7String.cs
  45. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/S7StringAttribute.cs
  46. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/S7WString.cs
  47. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Single.cs
  48. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/String.cs
  49. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/StringEx.cs
  50. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Struct.cs
  51. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Timer.cs
  52. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/TypeHelper.cs
  53. 0 0
      src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Word.cs
  54. 0 3
      src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Properties/AssemblyInfo.cs
  55. BIN
      src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Properties/S7.Net.snk
  56. 137 0
      src/YSAI.DAQ/YSAI.S7/service/Common/DataConvert.cs
  57. 114 0
      src/YSAI.DAQ/YSAI.S7/service/Common/DataPersist.cs
  58. 1 1
      src/YSAI.DAQ/YSAI.Modbus/service/ServerSocketBase.cs
  59. 79 0
      src/YSAI.DAQ/YSAI.S7/service/Common/SiemensConstant.cs
  60. 250 0
      src/YSAI.DAQ/YSAI.S7/service/S7Service.cs

+ 2 - 1
readme.txt

@@ -12,7 +12,8 @@ OPC DA Http
 ModbusClient TCP/UDP/RTU/Ascii
 ModbusService  TCP/RTU/Ascii
 
-S7 所有CPU型号
+S7Client 所有CPU型号
+S7Service
 
 Kafka Admin/Consumer/Producer
 

+ 1 - 1
src/YSAI.DAQ/YSAI.Core/interface/only/IS7.cs

@@ -10,5 +10,5 @@ namespace YSAI.Core.@interface.only
     /// <summary>
     /// S7接口
     /// </summary>
-    public interface IS7 : IOn, IOff, IRead, IWrite, IDisposable { }
+    public interface IS7Client : IOn, IOff, IRead, IWrite, IDisposable { }
 }

+ 44 - 0
src/YSAI.DAQ/YSAI.Modbus/service/Common/ServerSocketBase.cs

@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Net.Sockets;
+using System.Text;
+
+namespace YSAI.Modbus.service.Common
+{
+    /// <summary>
+    /// ServerSocket基类
+    /// </summary>
+    public class ServerSocketBase
+    {
+        /// <summary>
+        /// 分批缓冲区大小
+        /// </summary>
+        protected const int BufferSize = 4096;
+
+        /// <summary>
+        /// 读取
+        /// </summary>
+        /// <param name="socket">socket</param>
+        /// <param name="receiveCount">读取长度</param>
+        /// <returns></returns>
+        protected byte[] SocketRead(Socket socket, int receiveCount)
+        {
+            byte[] receiveBytes = new byte[receiveCount];
+            int receiveFinish = 0;
+            while (receiveFinish < receiveCount)
+            {
+                // 分批读取
+                int receiveLength = receiveCount - receiveFinish >= BufferSize ? BufferSize : receiveCount - receiveFinish;
+                var readLeng = socket.Receive(receiveBytes, receiveFinish, receiveLength, SocketFlags.None);
+                if (readLeng == 0)
+                {
+                    if (socket.Connected) socket.Shutdown(SocketShutdown.Both);
+                    socket.Close();
+                    throw new Exception("连接已断开");
+                }
+                receiveFinish += readLeng;
+            }
+            return receiveBytes;
+        }
+    }
+}

+ 7 - 7
src/YSAI.DAQ/YSAI.S7/S7Data.cs

@@ -5,10 +5,10 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace YSAI.S7
+namespace YSAI.S7.client
 {
 
-    public class S7Data
+    public class S7ClientData
     {
         /// <summary>
         /// 基础数据
@@ -48,11 +48,11 @@ namespace YSAI.S7
             {
                 Basics? obj = Obj as Basics;
                 if (obj == null) return false;
-                if (obj.Ip == this.Ip&&
-                    obj.Port == this.Port &&
-                    obj.S7CpuType == this.S7CpuType &&
-                    obj.Rack == this.Rack &&
-                    obj.Slot == this.Slot)
+                if (obj.Ip == Ip &&
+                    obj.Port == Port &&
+                    obj.S7CpuType == S7CpuType &&
+                    obj.Rack == Rack &&
+                    obj.Slot == Slot)
                 {
                     return true;
                 }

+ 10 - 10
src/YSAI.DAQ/YSAI.S7/S7Operate.cs

@@ -9,23 +9,23 @@ using YSAI.Core.virtualAddress;
 using YSAI.Log;
 using YSAI.Unility;
 
-namespace YSAI.S7
+namespace YSAI.S7.client
 {
     /// <summary>
     /// s7操作
     /// </summary>
-    public class S7Operate:IBase,IS7
+    public class S7ClientOperate : IBase, IS7Client
     {
         private static readonly object Lock = new object();  //锁
-        private static List<S7Operate> ThisObjList = new List<S7Operate>(); //自身对象集合
+        private static List<S7ClientOperate> ThisObjList = new List<S7ClientOperate>(); //自身对象集合
 
         /// <summary>
         /// 单例模式
         /// </summary>
         /// <returns></returns>
-        public static S7Operate Instance(S7Data.Basics basics)
+        public static S7ClientOperate Instance(S7ClientData.Basics basics)
         {
-            S7Operate? exp = ThisObjList.FirstOrDefault(c => c.basics.Equals(basics));
+            S7ClientOperate? exp = ThisObjList.FirstOrDefault(c => c.basics.Equals(basics));
             if (exp == null)
             {
                 lock (Lock)
@@ -36,7 +36,7 @@ namespace YSAI.S7
                     }
                     else
                     {
-                        S7Operate exp2 = new S7Operate(basics);
+                        S7ClientOperate exp2 = new S7ClientOperate(basics);
                         ThisObjList.Add(exp2);
                         return exp2;
                     }
@@ -49,7 +49,7 @@ namespace YSAI.S7
         /// 构造函数
         /// </summary>
         /// <param name="basics"></param>
-        public S7Operate(S7Data.Basics basics)
+        public S7ClientOperate(S7ClientData.Basics basics)
         {
             this.basics = basics;
         }
@@ -57,7 +57,7 @@ namespace YSAI.S7
         /// <summary>
         /// 基础数据
         /// </summary>
-        private S7Data.Basics basics { get; set; }
+        private S7ClientData.Basics basics { get; set; }
         /// <summary>
         /// 虚拟地址
         /// </summary>
@@ -87,7 +87,7 @@ namespace YSAI.S7
             {
                 if (PlcS7 == null)
                 {
-                    PlcS7 = new Plc(basics.S7CpuType, basics.Ip, basics.Port, (Int16)basics.Rack, (Int16)basics.Slot);
+                    PlcS7 = new Plc(basics.S7CpuType, basics.Ip, basics.Port, (short)basics.Rack, (short)basics.Slot);
                     PlcS7.Open();
                     if (!PlcS7.IsConnected)
                     {
@@ -178,7 +178,7 @@ namespace YSAI.S7
                         }
 
                         //数据处理
-                        AddressValue addressValue = YSAI.Core.data.Dispose.Execute(item, Value);
+                        AddressValue addressValue = Core.data.Dispose.Execute(item, Value);
 
                         //数据添加
                         param.AddOrUpdate(item.Address, addressValue, (k, v) => addressValue);

src/YSAI.DAQ/YSAI.S7/lib/S7.Net/COTP.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/COTP.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Compat/TcpClientMixins.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Compat/TcpClientMixins.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Conversion.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Conversion.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Enums.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Enums.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Helper/MemoryStreamExtension.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Helper/MemoryStreamExtension.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Internal/TaskQueue.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Internal/TaskQueue.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/InvalidDataException.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/InvalidDataException.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/PLC.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/PLC.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/PLCAddress.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/PLCAddress.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/PLCExceptions.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/PLCExceptions.cs


+ 3 - 2
src/YSAI.DAQ/YSAI.S7/lib/S7.Net/PLCHelpers.cs

@@ -4,6 +4,7 @@ using S7.Net.Types;
 using System.Collections.Generic;
 using System.Linq;
 using DateTime = S7.Net.Types.DateTime;
+using Timer = S7.Net.Types.Timer;
 
 namespace S7.Net
 {
@@ -129,9 +130,9 @@ namespace S7.Net
 
                 case VarType.Timer:
                     if (varCount == 1)
-                        return S7.Net.Types.Timer.FromByteArray(bytes);
+                        return Timer.FromByteArray(bytes);
                     else
-                        return S7.Net.Types.Timer.ToArray(bytes);
+                        return Timer.ToArray(bytes);
                 case VarType.Counter:
                     if (varCount == 1)
                         return Counter.FromByteArray(bytes);

src/YSAI.DAQ/YSAI.S7/lib/S7.Net/PlcAsynchronous.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/PlcAsynchronous.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/PlcException.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/PlcException.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/PlcSynchronous.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/PlcSynchronous.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Protocol/ConnectionRequest.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Protocol/ConnectionRequest.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Protocol/ReadWriteErrorCode.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Protocol/ReadWriteErrorCode.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Protocol/S7/DataItemAddress.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Protocol/S7/DataItemAddress.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Protocol/S7WriteMultiple.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Protocol/S7WriteMultiple.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Protocol/Serialization.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Protocol/Serialization.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Protocol/Tsap.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Protocol/Tsap.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Protocol/TsapPair.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Protocol/TsapPair.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/StreamExtensions.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/StreamExtensions.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/TPKT.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/TPKT.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/Bit.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Bit.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/Boolean.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Boolean.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/Byte.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Byte.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/ByteArray.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/ByteArray.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/Class.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Class.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/Counter.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Counter.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/DInt.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/DInt.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/DWord.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/DWord.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/DataItem.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/DataItem.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/DateTime.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/DateTime.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/DateTimeLong.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/DateTimeLong.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/Double.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Double.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/Int.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Int.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/LReal.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/LReal.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/Real.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Real.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/S7String.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/S7String.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/S7StringAttribute.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/S7StringAttribute.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/S7WString.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/S7WString.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/Single.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Single.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/String.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/String.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/StringEx.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/StringEx.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/Struct.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Struct.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/Timer.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Timer.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/TypeHelper.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/TypeHelper.cs


src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Types/Word.cs → src/YSAI.DAQ/YSAI.S7/client/lib/S7.Net/Types/Word.cs


+ 0 - 3
src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Properties/AssemblyInfo.cs

@@ -1,3 +0,0 @@
-using System.Runtime.CompilerServices;
-
-[assembly: InternalsVisibleTo("S7.Net.UnitTest, PublicKey=00240000048000009400000006020000002400005253413100040000010001002d1032db55f60d64bf90ea1cc2247b5a8b9b6168a07bcd464a07ce2e425d027ff9409a64ba0e3f37718e14c50cf964d0d921e5ae8b8d74bd8a82431794f897cebf0ee668feb2ccd030153611b2808fcb7785c5e5136a98e0ec23de3c1ed385d2026c26e4bed5805ff9db7e0544f59b1f19d369d43403a624586795926e38c48d")]

BIN
src/YSAI.DAQ/YSAI.S7/lib/S7.Net/Properties/S7.Net.snk


+ 137 - 0
src/YSAI.DAQ/YSAI.S7/service/Common/DataConvert.cs

@@ -0,0 +1,137 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace YSAI.S7.service.Common
+{
+    /// <summary>
+    /// 数据转换
+    /// </summary>
+    public static class DataConvert
+    {
+        /// <summary>
+        /// 字节数组转16进制字符
+        /// </summary>
+        /// <param name="byteArray"></param>
+        /// <returns></returns>
+        public static string ByteArrayToString(this byte[] byteArray)
+        {
+            return string.Join(" ", byteArray.Select(t => t.ToString("X2")));
+        }
+
+        /// <summary>
+        /// 16进制字符串转字节数组
+        /// </summary>
+        /// <param name="str"></param>
+        /// <param name="strict">严格模式(严格按两个字母间隔一个空格)</param>
+        /// <returns></returns>
+        public static byte[] StringToByteArray(this string str, bool strict = true)
+        {
+            if (string.IsNullOrWhiteSpace(str) || str.Trim().Replace(" ", "").Length % 2 != 0)
+                throw new ArgumentException("请传入有效的参数");
+
+            if (strict)
+            {
+                return str.Split(' ').Where(t => t?.Length == 2).Select(t => Convert.ToByte(t, 16)).ToArray();
+            }
+            else
+            {
+                str = str.Trim().Replace(" ", "");
+                var list = new List<byte>();
+                for (int i = 0; i < str.Length; i++)
+                {
+                    var string16 = str[i].ToString() + str[++i].ToString();
+                    list.Add(Convert.ToByte(string16, 16));
+                }
+                return list.ToArray();
+            }
+        }
+
+        /// <summary>
+        /// Asciis字符串数组字符串装字节数组
+        /// </summary>
+        /// <param name="str"></param>
+        /// <param name="strict"></param>
+        /// <returns></returns>
+        public static byte[] AsciiStringToByteArray(this string str, bool strict = true)
+        {
+            if (string.IsNullOrWhiteSpace(str) || str.Trim().Replace(" ", "").Length % 2 != 0)
+                throw new ArgumentException("请传入有效的参数");
+
+            if (strict)
+            {
+                List<string> stringList = new List<string>();
+                foreach (var item in str.Split(' '))
+                {
+                    stringList.Add(((char)Convert.ToByte(item, 16)).ToString());
+                }
+                return string.Join("", stringList).StringToByteArray(false);
+            }
+            else
+            {
+                str = str.Trim().Replace(" ", "");
+                var stringList = new List<string>();
+                for (int i = 0; i < str.Length; i++)
+                {
+                    var stringAscii = str[i].ToString() + str[++i].ToString();
+                    stringList.Add(((char)Convert.ToByte(stringAscii, 16)).ToString());
+                }
+                return string.Join("", stringList).StringToByteArray(false);
+            }
+        }
+
+        /// <summary>
+        /// Asciis数组字符串装字节数组
+        /// 如:30 31 =》 00 01
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static byte[] AsciiArrayToByteArray(this byte[] str)
+        {
+            if (!str?.Any() ?? true)
+                throw new ArgumentException("请传入有效的参数");
+
+            List<string> stringList = new List<string>();
+            foreach (var item in str)
+            {
+                stringList.Add(((char)item).ToString());
+            }
+            return string.Join("", stringList).StringToByteArray(false);
+        }
+
+        /// <summary>
+        /// 字节数组转换成Ascii字节数组
+        /// 如:00 01 => 30 31
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static byte[] ByteArrayToAsciiArray(this byte[] str)
+        {
+            return Encoding.ASCII.GetBytes(string.Join("", str.Select(t => t.ToString("X2"))));
+        }
+
+        /// <summary>
+        /// Int转二进制
+        /// </summary>
+        /// <param name="value"></param>
+        /// <param name="minLength">补0长度</param>
+        /// <returns></returns>
+        public static string IntToBinaryArray(this int value, int minLength = 0)
+        {
+            //Convert.ToString(12,2); // 将12转为2进制字符串,结果 “1100”
+            return Convert.ToString(value, 2).PadLeft(minLength, '0');
+        }
+
+        /// <summary>
+        /// 二进制转Int
+        /// </summary>
+        /// <param name="value"></param>
+        /// <returns></returns>
+        public static int BinaryArrayToInt(this string value)
+        {
+            //Convert.ToInt("1100",2); // 将2进制字符串转为整数,结果 12
+            return Convert.ToInt32(value, 2);
+        }
+    }
+}

+ 114 - 0
src/YSAI.DAQ/YSAI.S7/service/Common/DataPersist.cs

@@ -0,0 +1,114 @@
+using Newtonsoft.Json;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace YSAI.S7.service.Common
+{
+    public class DataPersist
+    {
+        string prefix;
+        static ConcurrentDictionary<string, string> data = new ConcurrentDictionary<string, string>();
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="prefix">前缀</param> 
+        public DataPersist(string prefix)
+        {
+            this.prefix = $"iot_{prefix}_";
+        }
+
+        /// <summary>
+        /// 读
+        /// </summary>
+        /// <param name="key"></param>
+        /// <returns></returns>
+        public string Read(string key)
+        {
+            key = prefix + key;
+            if (data.ContainsKey(key))
+            {
+                return data[key];
+            }
+            return string.Empty;
+        }
+
+        public string Read(int key)
+        {
+            return Read(key.ToString());
+        }
+
+        /// <summary>
+        /// 写
+        /// </summary>
+        /// <param name="key"></param>
+        /// <param name="value"></param>
+        public void Write(string key, string value)
+        {
+            key = prefix + key;
+            if (data.ContainsKey(key))
+            {
+                data[key] = value;
+            }
+            else
+            {
+                data.TryAdd(key, value);
+            }
+        }
+
+        public void Write(int key, string value)
+        {
+            Write(key.ToString(), value);
+        }
+
+        /// <summary>
+        /// 清空
+        /// </summary>
+        public static void Clear()
+        {
+            data = new ConcurrentDictionary<string, string>();
+
+            var filePath = @"C:\IoTClient\ConnectionConfig.Data";
+            if (File.Exists(filePath)) File.Delete(filePath);
+        }
+
+        /// <summary>
+        /// 保存数据
+        /// </summary>
+        public static void SaveData()
+        {
+            var path = @"C:\IoTClient";
+            var filePath = path + @"\IoTClient.Data";
+            var dataString = JsonConvert.SerializeObject(data);
+            using (FileStream fileStream = new FileStream(filePath, FileMode.Create))
+            {
+                using (StreamWriter sw = new StreamWriter(fileStream))
+                {
+                    sw.Write(dataString);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 初始化加载数据
+        /// </summary>
+        public static void LoadData()
+        {
+            var dataString = string.Empty;
+            var path = @"C:\IoTClient";
+            var filePath = path + @"\IoTClient.Data";
+            if (File.Exists(filePath))
+                dataString = File.ReadAllText(filePath);
+            else
+            {
+                if (!Directory.Exists(path))
+                    Directory.CreateDirectory(path);
+                File.SetAttributes(path, FileAttributes.Hidden);
+            }
+            if (!string.IsNullOrWhiteSpace(dataString))
+                data = JsonConvert.DeserializeObject<ConcurrentDictionary<string, string>>(dataString);
+        }
+    }
+}

+ 1 - 1
src/YSAI.DAQ/YSAI.Modbus/service/ServerSocketBase.cs

@@ -3,7 +3,7 @@ using System.Collections.Generic;
 using System.Net.Sockets;
 using System.Text;
 
-namespace YSAI.Modbus.service
+namespace YSAI.S7.service.Common
 {
     /// <summary>
     /// ServerSocket基类

+ 79 - 0
src/YSAI.DAQ/YSAI.S7/service/Common/SiemensConstant.cs

@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace YSAI.S7.service.Common
+{
+    /// <summary>
+    /// Siemens命令常量
+    /// </summary>
+    public class SiemensConstant
+    {
+        /// <summary>
+        /// Head头读取长度
+        /// </summary>
+        public static readonly ushort InitHeadLength = 4;
+
+        /// <summary>
+        /// 第一次初始化指令交互报文
+        /// </summary>
+        public static readonly byte[] Command1 = new byte[22]
+        {
+            0x03,0x00,0x00,0x16,0x11,0xE0,0x00,0x00,
+            0x00,0x01,0x00,0xC0,0x01,0x0A,0xC1,0x02,
+            0x01,0x02,0xC2,0x02,0x01,0x00
+        };
+
+        /// <summary>
+        /// 第二次初始化指令交互报文
+        /// </summary>
+        public static readonly byte[] Command2 = new byte[25]
+        {
+            0x03,0x00,0x00,0x19,0x02,0xF0,0x80,0x32,
+            0x01,0x00,0x00,0x04,0x00,0x00,0x08,0x00,
+            0x00,0xF0,0x00,0x00,0x01,0x00,0x01,0x01,0xE0
+        };
+
+        /// <summary>
+        /// 第一次初始化指令交互报文
+        /// </summary>
+        public static readonly byte[] Command1_200Smart = new byte[22]
+        {
+            0x03,0x00,0x00,0x16,0x11,0xE0,0x00,0x00,
+            0x00,0x01,0x00,0xC1,0x02,0x10,0x00,0xC2,
+            0x02,0x03,0x00,0xC0,0x01,0x0A
+        };
+
+        /// <summary>
+        /// 第二次初始化指令交互报文
+        /// </summary>
+        public static readonly byte[] Command2_200Smart = new byte[25]
+        {
+            0x03,0x00,0x00,0x19,0x02,0xF0,0x80,0x32,
+            0x01,0x00,0x00,0xCC,0xC1,0x00,0x08,0x00,
+            0x00,0xF0,0x00,0x00,0x01,0x00,0x01,0x03,0xC0
+        };
+
+        /// <summary>
+        /// 第一次初始化指令交互报文
+        /// </summary>
+        public static readonly byte[] Command1_200 = new byte[]
+        {
+            0x03,0x00,0x00,0x16,0x11,0xE0,0x00,0x00,
+            0x00,0x01,0x00,0xC1,0x02,0x4D,0x57,0xC2,
+            0x02,0x4D,0x57,0xC0,0x01,0x09
+        };
+
+        /// <summary>
+        /// 第二次初始化指令交互报文
+        /// </summary>
+        public static readonly byte[] Command2_200 = new byte[]
+        {
+            0x03,0x00,0x00,0x19,0x02,0xF0,0x80,0x32,
+            0x01,0x00,0x00,0x00,0x00,0x00,0x08,0x00,
+            0x00,0xF0,0x00,0x00,0x01,0x00,0x01,0x03,0xC0
+        };
+    }
+}

+ 250 - 0
src/YSAI.DAQ/YSAI.S7/service/S7Service.cs

@@ -0,0 +1,250 @@
+using YSAI.S7.service.Common;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+
+namespace IoTServer.Servers.PLC
+{
+    /// <summary>
+    /// 西门子PLC 服务端模拟
+    /// </summary>
+    public class S7Service : ServerSocketBase
+    {
+        private Socket socketServer;
+        private string ip;
+        private int port;
+        List<Socket> sockets = new List<Socket>();
+        DataPersist dataPersist;
+        public S7Service(int port, string ip = null)
+        {
+            this.ip = ip;
+            this.port = port;
+            dataPersist = new DataPersist("SiemensServer");
+        }
+
+        /// <summary>
+        /// 启动服务
+        /// </summary>
+        public void Start()
+        {
+            //1 创建Socket对象
+            socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+
+            //2 绑定ip和端口 
+            var ipaddress = string.IsNullOrWhiteSpace(ip) ? IPAddress.Any : IPAddress.Parse(ip);
+            IPEndPoint ipEndPoint = new IPEndPoint(ipaddress, port);
+            socketServer.Bind(ipEndPoint);
+
+            //3、开启侦听(等待客户机发出的连接),并设置最大客户端连接数为10
+            socketServer.Listen(100);
+
+            Task.Run(() => { Accept(socketServer); });
+        }
+
+        /// <summary>
+        /// 停止服务
+        /// </summary>
+        public void Stop()
+        {
+            if (socketServer?.Connected ?? false) socketServer.Shutdown(SocketShutdown.Both);
+            socketServer?.Close();
+        }
+
+        /// <summary>
+        /// 客户端连接到服务端
+        /// </summary>
+        /// <param name="socket"></param>
+        void Accept(Socket socket)
+        {
+            while (true)
+            {
+                try
+                {
+                    Socket newSocket = null;
+                    try
+                    {
+                        //阻塞等待客户端连接
+                        newSocket = socket.Accept();
+                        sockets.Add(newSocket);
+                        Task.Run(() => { Receive(newSocket); });
+                    }
+                    catch (SocketException)
+                    {
+                        foreach (var item in sockets)
+                        {
+                            if (item?.Connected ?? false) item.Shutdown(SocketShutdown.Both);
+                            item?.Close();
+                        }
+                    }
+                }
+                catch (SocketException ex)
+                {
+                    if (ex.SocketErrorCode != SocketError.Interrupted)
+                        throw ex;
+                }
+
+            }
+        }
+
+        /// <summary>
+        /// 接收客户端发送的消息
+        /// </summary>
+        /// <param name="newSocket"></param>
+        void Receive(Socket newSocket)
+        {
+            while (newSocket.Connected)
+            {
+                try
+                {
+                    byte[] requetData1 = new byte[4];
+                    //读取客户端发送过来的数据                   
+                    requetData1 = SocketRead(newSocket, requetData1.Length);
+                    byte[] requetData2 = new byte[requetData1[2] * 256 + requetData1[3] - 4];
+                    requetData2 = SocketRead(newSocket, requetData2.Length);
+                    var requetData = requetData1.Concat(requetData2).ToArray();
+
+                    //如果是连接的时候发送的两次初始化指令 则直接跳过
+                    if (requetData[3] == SiemensConstant.Command1_200Smart.Length || requetData[3] == SiemensConstant.Command2_200Smart.Length)
+                    {
+                        newSocket.Send(requetData);
+                        continue;
+                    }
+
+                    switch (requetData[17])
+                    {
+                        //读
+                        case 4:
+                            {
+                                //数据块个数
+                                var dbNumber = requetData[18];
+                                //读取数据总长度
+                                var readLength = 0;
+
+                                for (int i = 0; i < dbNumber; i++)
+                                {
+                                    //访问数据的个数,以byte为单位
+                                    var byteLength = requetData[23 + i * 12] * 256 + requetData[24 + i * 12];
+                                    if (byteLength == 1 && i < dbNumber - 1)//非最后一个bit/byte,需要补全。
+                                        byteLength += 1;
+                                    readLength += byteLength;
+                                }
+
+                                var dataContent = new byte[4 * dbNumber + readLength];//数据报文总长度
+                                var cursor = 0;
+                                for (int i = 0; i < dbNumber; i++)
+                                {
+                                    //DB块的偏移量
+                                    var beginAddress = requetData[28 + i * 12] * 256 * 256 + requetData[29 + i * 12] * 256 + requetData[30 + i * 12];
+                                    //访问数据的个数
+                                    var byteLength = requetData[23 + i * 12] * 256 + requetData[24 + i * 12];
+                                    if (byteLength == 1 && i < dbNumber - 1)//非最后一个bit/byte,需要补全。
+                                        byteLength += 1;
+                                    //是否是bit类型
+                                    var isBit = requetData[22 + i * 12] == 0x01;
+                                    //访问数据块的类型(V、I、DB...)
+                                    var dbType = requetData[27 + i * 12];
+                                    var dataKey = $"s200-{dbType}";
+                                    var dataValue = dataPersist.Read(dataKey);
+                                    var byteArray = JsonConvert.DeserializeObject<byte[]>(dataValue) ?? new byte[65536];
+
+                                    //DataConvert.StringToByteArray("FF 09 00 04").CopyTo(dataContent, cursor);
+                                    dataContent[0 + cursor] = 0xFF;
+                                    //dataContent[1 + cursor] = readLength == 1 ? (byte)0x03 : (byte)0x04;//04 byte(字节) 03bit(位)
+                                    dataContent[1 + cursor] = isBit ? (byte)0x03 : (byte)0x04;//04 byte(字节) 03bit(位)
+                                    dataContent[2 + cursor] = (byte)(byteLength / 256);//后半截数据数的Length
+                                    dataContent[3 + cursor] = (byte)(byteLength % 256);//后半截数据数的Length
+                                    if (isBit)//按bit
+                                    {
+                                        var bitOffset = beginAddress % 8;
+                                        byte bitOffsetValue = (byte)Math.Pow(2, bitOffset);
+                                        var oldBitValue = byteArray[beginAddress / 8];
+                                        //转成bit的形式所需的返回值
+                                        var bitValue = (oldBitValue & bitOffsetValue) != 0 ? 0x01 : 0x00;
+                                        //[4 + cursor]返回给客户端的数据
+                                        new byte[] { (byte)bitValue }.CopyTo(dataContent, 4 + cursor);
+                                    }
+                                    else
+                                        //[4 + cursor]返回给客户端的数据
+                                        Buffer.BlockCopy(byteArray, beginAddress / 8, dataContent, 4 + cursor, byteLength);
+
+                                    cursor += 4 + byteLength;
+                                }
+
+                                byte[] responseData = new byte[21 + dataContent.Length];
+                                DataConvert.StringToByteArray("03 00 00 1A 02 F0 80 32 03 00 00 00 01 00 02 00 00 00 00 04 01").CopyTo(responseData, 0);
+                                responseData[8] = 0x03;//1  客户端发送命令 3 服务器回复命令 
+                                responseData[2] = (byte)(responseData.Length / 256);//返回数据长度
+                                responseData[3] = (byte)(responseData.Length % 256);
+                                responseData[15] = (byte)(requetData.Length / 256);//读取数据长度
+                                responseData[16] = (byte)(requetData.Length % 256);
+                                responseData[20] = requetData[18];//读取数据块个数
+                                dataContent.CopyTo(responseData, 21);
+                                newSocket.Send(responseData);
+                            }
+                            break;
+                        //写
+                        case 5:
+                            {
+                                var writesLength = requetData[18];//写如数据的个数 Item count
+                                int cursor = 0;
+                                for (int i = 0; i < writesLength; i++)
+                                {
+                                    //DB块的偏移量(存储数据的地址)
+                                    var beginAddress = requetData[28 + i * 12] * 256 * 256 + requetData[29 + i * 12] * 256 + requetData[30 + i * 12];
+                                    //访问数据块的类型(V、I、DB...)
+                                    var dbType = requetData[27 + i * 12];
+                                    var dataKey = $"s200-{dbType}";
+
+                                    //Data之前的下标
+                                    var dataBeforeIndex = 18 + writesLength * 12;
+                                    //[2 + index]是否是bit类型
+                                    var isBit = requetData[dataBeforeIndex + 4 * (i + 1) - 2 + cursor] == 0x03;
+                                    var coefficient = isBit ? 1 : 8;
+                                    //初始化要写入的字节数组长度
+                                    var writeValue = new byte[requetData[dataBeforeIndex + 4 * (i + 1) + cursor] / coefficient];
+                                    //开始写入的地址(报文中的数据的位置)
+                                    var requetBeginLocation = dataBeforeIndex + 4 * (i + 1) + cursor + 1;
+
+                                    //非最后一个bit,需要补全。
+                                    if (writeValue.Length == 1 && i < writesLength - 1)
+                                        cursor++;
+                                    cursor += writeValue.Length;
+                                    //数据赋值到writeValue中
+                                    Buffer.BlockCopy(requetData, requetBeginLocation, writeValue, 0, writeValue.Length);
+
+                                    var byteArray = JsonConvert.DeserializeObject<byte[]>(dataPersist.Read(dataKey)) ?? new byte[65536];
+                                    if (isBit)
+                                    {
+                                        var oldBitValue = byteArray[beginAddress / 8];
+                                        var bitOffset = beginAddress % 8;
+                                        byte bitOffsetValue = (byte)Math.Pow(2, bitOffset);
+                                        if (writeValue[0] == 0x01)//true
+                                            oldBitValue = (byte)(oldBitValue | bitOffsetValue);//组合bitOffsetValue
+                                        else//false
+                                            oldBitValue = (byte)(oldBitValue & ~bitOffsetValue);//去掉bitOffsetValue
+                                        new byte[] { oldBitValue }.CopyTo(byteArray, beginAddress / 8);
+                                    }
+                                    else
+                                        writeValue.CopyTo(byteArray, beginAddress / 8);
+
+                                    //存储字节数据到内存
+                                    dataPersist.Write(dataKey, JsonConvert.SerializeObject(byteArray));
+                                }
+
+                                byte[] responseData1 = new byte[22];
+                                DataConvert.StringToByteArray("03 00 00 16 02 F0 80 32 03 00 00 00 01 00 02 00 01 00 00 05 01 FF").CopyTo(responseData1, 0);
+                                newSocket.Send(responseData1);
+                            }
+                            break;
+                    }
+                }
+                catch { }
+            }
+        }
+    }
+}
+