Compare commits

...

18 Commits

Author SHA1 Message Date
Kimdiego2098
80cd6b693e 发行版本5.0.1.0 2024-02-23 13:09:25 +08:00
Kimdiego2098
04721a12b1 发行版本5.0.1.0 2024-02-23 13:03:53 +08:00
Kimdiego2098
64e22c0e46 取消默认PublishReadyToRun 2024-02-23 13:03:46 +08:00
Kimdiego2098
d5a70c5b08 Revert "发行版本5.0.1"
This reverts commit eaac7b6bcf.
2024-02-23 13:03:12 +08:00
Kimdiego2098
eaac7b6bcf 发行版本5.0.1 2024-02-23 12:58:25 +08:00
Kimdiego2098
b062a491cd 调整代码 2024-02-23 12:36:28 +08:00
Kimdiego2098
1e868517bb 默认启用PublishReadyToRun 2024-02-23 11:02:56 +08:00
Kimdiego2098
7b2a93a2d7 更新版本 2024-02-22 18:13:10 +08:00
Kimdiego2098
f57f0447c6 完善modbus写入保持寄存器某一位,比如400001.0的功能 2024-02-22 18:12:58 +08:00
Kimdiego2098
7126ff881e 更新版本 2024-02-22 17:52:32 +08:00
Kimdiego2098
e28da4b165 modbusTcp返回错误码时没有验证头部id 2024-02-22 17:52:20 +08:00
Kimdiego2098
92d9b91f7c 更新依赖包 2024-02-22 17:31:27 +08:00
Kimdiego2098
149c4a30c0 写入表达式支持json数组 2024-02-21 14:50:21 +08:00
Kimdiego2098
84e62062ec 表达式转换支持Json 2024-02-21 13:35:41 +08:00
Kimdiego2098
dc1fb74850 更新readme 2024-02-21 11:33:00 +08:00
Kimdiego2098
00c6010789 更新readme 2024-02-21 11:07:00 +08:00
Kimdiego2098
5d35c058e0 更新readme 2024-02-21 11:05:54 +08:00
Kimdiego2098
1522a521f6 更新readme 2024-02-21 10:59:53 +08:00
12 changed files with 135 additions and 97 deletions

View File

@@ -14,9 +14,34 @@
[ThingsGateway](https://diego2098.gitee.io/thingsgateway-docs/) 文档。
### 插件列表
#### 采集插件
| 插件名称 | 备注 |
|-------|-------|
| ModbusMaster | Rtu/Tcp报文格式支持串口/Tcp/Udp链路 |
| S7 | 西门子PLC S7系列 |
| Dlt6452007 | Master支持串口/Tcp/Udp链路 |
| OpcDaClient | 64位编译 |
| OpcUaClient | 支持证书登录扩展对象Json读写 |
#### 业务插件
| 插件名称 | 备注 |
|-------|-------|
| ModbusSlave | Rtu/Tcp报文格式支持串口/Tcp/Udp链路支持Rpc反写 |
| OpcUaServer | OpcUa服务端支持Rpc反写 |
| Mqtt Client | Mqtt客户端支持Rpc反写脚本自定义上传内容 |
| Mqtt Server | Mqtt服务端支持WebSocket支持Rpc反写脚本自定义上传内容 |
| Kafka Client | 数据生产,脚本自定义上传内容 |
| RabbitMQ Client | 数据生产,脚本自定义上传内容 |
| SqlDB | 关系数据库存储,支持历史存储和实时数据更新 |
| SqlHisAlarm | 报警历史数据关系数据库存储 |
| TDengineDB | 时序数据库存储 |
| QuestDB | 时序数据库存储 |
## 协议
[ThingsGateway](https://gitee.com/diego2098/ThingsGateway) 采用 [Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.zh) 开源协议。
[ThingsGateway](https://gitee.com/diego2098/ThingsGateway) 采用 [Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE) 开源协议。
## 演示
@@ -28,11 +53,15 @@
## 赞助
[ThingsGateway赞助途径](https://diego2098.gitee.io/thingsgateway-docs/docs/donate)
[ThingsGateway赞助途径](https://diego2098.gitee.io/thingsgateway-docs/docs/1000)
## 社区
QQ群605534569
QQ群605534569 [跳转](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569)
## Pro插件
[插件列表](https://diego2098.gitee.io/thingsgateway-docs/docs/1001)

View File

@@ -6,7 +6,7 @@
<LangVersion>11.0</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version>5.0.0.22</Version>
<Version>5.0.1.0</Version>
<Authors>Diego</Authors>
<Company>Diego</Company>
<Product>Diego</Product>

View File

@@ -2,12 +2,12 @@
<Import Project="$(SolutionDir)PackNuget.props" />
<ItemGroup>
<PackageReference Include="Furion.Pure" Version="4.9.1.31" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.1.31" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.1.31" />
<PackageReference Include="Furion.Pure" Version="4.9.1.32" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.1.32" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.1.32" />
<PackageReference Include="NewLife.Redis" Version="5.6.2024.203" />
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
<PackageReference Include="LiteDB" Version="5.0.18" />
<PackageReference Include="LiteDB" Version="5.0.19" />
</ItemGroup>
<ItemGroup>

View File

@@ -29,14 +29,28 @@ public static class ExpressionEvaluatorExtension
{
return rawvalue;
}
//JArray内部转换
//
//JArray newValue = new JArray(raw);
//for (int i = 0; i < raw.Count; i++)
//{
//newValue[i] = JToken.FromObject(Extensions.Value<int>(raw[i]) * 1000);
//}
//return newValue;
//
ExpressionEvaluator expressionEvaluator = new();
expressionEvaluator.OptionScriptNeedSemicolonAtTheEndOfLastExpression = false;
expressionEvaluator.PreEvaluateVariable += Evaluator_PreEvaluateVariable;
expressionEvaluator.Assemblies.Add(typeof(Newtonsoft.Json.JsonConverter).Assembly);
expressionEvaluator.Namespaces.Add("Newtonsoft.Json.Linq");
expressionEvaluator.Namespaces.Add("Newtonsoft.Json");
expressionEvaluator.Variables = new Dictionary<string, object>()
{
{ "Raw", rawvalue},
{ "raw", rawvalue},
};
var value = expressionEvaluator.Evaluate(expressions);
var value = expressionEvaluator.ScriptEvaluate(expressions);
return value;
}

View File

@@ -395,7 +395,7 @@ public abstract class CollectBase : DriverBase
{
if (!string.IsNullOrEmpty(deviceVariable.WriteExpressions))
{
object rawdata = jToken is JValue jValue ? jValue.Value : jToken.ToString();
object rawdata = jToken is JValue jValue ? jValue.Value : jToken is JArray jArray ? jArray : jToken.ToString();
try
{
object data = deviceVariable.WriteExpressions.GetExpressionsResult(rawdata);
@@ -409,12 +409,11 @@ public abstract class CollectBase : DriverBase
}
//转换失败的变量不再写入
var result = await WriteValuesAsync(writeInfoLists.
var results1 = await WriteValuesAsync(writeInfoLists.
Where(a => !results.Any(b => b.Key == a.Key.Name)).
ToDictionary(item => item.Key, item => item.Value),
cancellationToken);
return result;
return results.Concat(results1).ToDictionary(a => a.Key, a => a.Value);
}
#endregion

View File

@@ -10,6 +10,9 @@
using Furion;
using SqlSugar;
using SqlSugar.TDengine;
using System.Reflection;
namespace ThingsGateway.Web.Entry;
@@ -26,6 +29,8 @@ public class SingleFilePublish : ISingleFilePublish
/// <inheritdoc/>
public string[] IncludeAssemblyNames()
{
InstanceFactory.CustomAssemblies =
new System.Reflection.Assembly[] { typeof(TDengineProvider).Assembly };
return new[]
{
"ThingsGateway.Foundation",

View File

@@ -16,6 +16,9 @@
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<ApplicationIcon>wwwroot\favicon.ico</ApplicationIcon>
<OutputPath>$(SolutionDir)bin\$(Configuration)\$(MSBuildProjectName)\</OutputPath>
<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
<!--<PublishReadyToRun>true</PublishReadyToRun>-->
<!--<ServerGarbageCollection>false</ServerGarbageCollection>-->
<!--<PlatformTarget>x86</PlatformTarget>-->
</PropertyGroup>

View File

@@ -99,7 +99,15 @@ public static class ModbusAddressHelper
modbusAddress.ReadFunction = 3;
break;
}
modbusAddress.Address = (double.Parse(address.Substring(1)) - 1).ToString();
string[] strArray = address.SplitStringByDelimiter();
if (strArray.Length > 1)
{
modbusAddress.Address = (ushort.Parse(strArray[0].Substring(1)) - 1).ToString() + '.' + strArray[1];
}
else
{
modbusAddress.Address = (ushort.Parse(strArray[0].Substring(1)) - 1).ToString();
}
}
}
}

View File

@@ -185,7 +185,6 @@ internal class ModbusHelper
if (send == null)
{
var result = new OperResult<byte[], FilterResult>() { Content = response.RemoveBegin(3), Content2 = FilterResult.Success };
result.ErrorMessage = FoundationConst.NotActiveQueryError;
return result;
}
if (send.Length == 0)
@@ -235,29 +234,6 @@ internal class ModbusHelper
return GetModbusData(send, data.RemoveLast(2));
}
/// <summary>
/// 获取读取报文
/// </summary>
internal static byte[] GetReadModbusCommand(string address, ushort length, byte station)
{
var mAddress = ModbusAddressHelper.ParseFrom(address, station);
return GetReadModbusCommand(mAddress, length);
}
/// <summary>
/// 获取写入字报文,根据地址识别功能码
/// </summary>
internal static byte[] GetWriteModbusCommand(string address, byte[] value, byte station)
{
var mAddress = ModbusAddressHelper.ParseFrom(address, station);
value = value.ArrayExpandToLengthEven();
//功能码或实际长度
if (value.Length > 2 || mAddress.WriteFunction == 16)
return GetWriteModbusCommand(mAddress, value);
else
return GetWriteOneModbusCommand(mAddress, value);
}
#region
/// <summary>

View File

@@ -39,9 +39,9 @@ internal class ModbusTcpDataHandleAdapter : ReadWriteDevicesSingleStreamDataHand
var result = ModbusHelper.GetModbusData(send?.RemoveBegin(6), response.RemoveBegin(6));
request.OperCode = result.OperCode;
request.ErrorMessage = result.ErrorMessage;
request.Sign = TouchSocketBitConverter.BigEndian.ToUInt16(response, 0);
if (result.IsSuccess)
{
request.Sign = TouchSocketBitConverter.BigEndian.ToUInt16(response, 0);
request.Content = result.Content;
}
return result.Content2;

View File

@@ -11,6 +11,8 @@
using System.ComponentModel;
using System.Text;
using ThingsGateway.Foundation.Extension.Generic;
using TouchSocket.Sockets;
namespace ThingsGateway.Foundation.Modbus;
@@ -18,6 +20,8 @@ namespace ThingsGateway.Foundation.Modbus;
/// <inheritdoc/>
public class ModbusMaster : ProtocolBase
{
private ModbusTypeEnum modbusType;
/// <inheritdoc/>
public ModbusMaster(IChannel channel) : base(channel)
{
@@ -26,8 +30,6 @@ public class ModbusMaster : ProtocolBase
WaitHandlePool.MaxSign = ushort.MaxValue;
}
private ModbusTypeEnum modbusType;
/// <summary>
/// Modbus类型
/// </summary>
@@ -171,9 +173,9 @@ public class ModbusMaster : ProtocolBase
{
try
{
var commandResult = ModbusHelper.GetReadModbusCommand(address, (ushort)length, Station);
return SendThenReturn(address, commandResult, cancellationToken);
var mAddress = ModbusAddressHelper.ParseFrom(address, Station);
var commandResult = ModbusHelper.GetReadModbusCommand(mAddress, (ushort)length);
return SendThenReturn(mAddress, commandResult, cancellationToken);
}
catch (Exception ex)
{
@@ -181,27 +183,14 @@ public class ModbusMaster : ProtocolBase
}
}
private OperResult<byte[]> SendThenReturn(string address, byte[] commandResult, CancellationToken cancellationToken)
{
if (Channel.ChannelType == ChannelTypeEnum.TcpService)
{
var mAddress = ModbusAddressHelper.ParseFrom(address, Station);
if (((TcpServiceBase)Channel).SocketClients.TryGetSocketClient($"ID={mAddress.SocketId}", out TgSocketClient? client))
return SendThenReturn(new SendMessage(commandResult), cancellationToken, client);
else
return new OperResult<byte[]>(FoundationConst.DtuNoConnectedWaining);
}
else
return SendThenReturn(new SendMessage(commandResult), cancellationToken);
}
/// <inheritdoc/>
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
{
try
{
var commandResult = ModbusHelper.GetReadModbusCommand(address, (ushort)length, Station);
return await SendThenReturnAsync(address, commandResult, cancellationToken);
var mAddress = ModbusAddressHelper.ParseFrom(address, Station);
var commandResult = ModbusHelper.GetReadModbusCommand(mAddress, (ushort)length);
return await SendThenReturnAsync(mAddress, commandResult, cancellationToken);
}
catch (Exception ex)
{
@@ -209,27 +198,20 @@ public class ModbusMaster : ProtocolBase
}
}
private Task<OperResult<byte[]>> SendThenReturnAsync(string address, byte[] commandResult, CancellationToken cancellationToken)
{
if (Channel.ChannelType == ChannelTypeEnum.TcpService)
{
var mAddress = ModbusAddressHelper.ParseFrom(address, Station);
if (((TcpServiceBase)Channel).SocketClients.TryGetSocketClient($"ID={mAddress.SocketId}", out TgSocketClient? client))
return SendThenReturnAsync(new SendMessage(commandResult), cancellationToken, client);
else
return Task.FromResult(new OperResult<byte[]>(FoundationConst.DtuNoConnectedWaining));
}
else
return SendThenReturnAsync(new SendMessage(commandResult), cancellationToken);
}
/// <inheritdoc/>
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
{
try
{
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
return SendThenReturn(address, commandResult, cancellationToken);
var mAddress = ModbusAddressHelper.ParseFrom(address, Station);
value = value.ArrayExpandToLengthEven();
byte[]? commandResult = null;
//功能码或实际长度
if (value.Length > 2 || mAddress.WriteFunction == 16)
commandResult = ModbusHelper.GetWriteModbusCommand(mAddress, value);
else
commandResult = ModbusHelper.GetWriteOneModbusCommand(mAddress, value);
return SendThenReturn(mAddress, commandResult, cancellationToken);
}
catch (Exception ex)
{
@@ -247,26 +229,26 @@ public class ModbusMaster : ProtocolBase
if (value.Length > 1 || mAddress.WriteFunction == 15)
{
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(mAddress, value, (ushort)value.Length);
return SendThenReturn(address, commandResult, cancellationToken);
return SendThenReturn(mAddress, commandResult, cancellationToken);
}
else
{
if (mAddress.BitIndex == null)
{
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(mAddress, value[0]);
return SendThenReturn(address, commandResult, cancellationToken);
return SendThenReturn(mAddress, commandResult, cancellationToken);
}
else if (mAddress.BitIndex < 16)
{
//比如40001.1
var read = ModbusHelper.GetReadModbusCommand(mAddress, 1);
var readData = SendThenReturn(address, read, cancellationToken);
var readData = SendThenReturn(mAddress, read, cancellationToken);
if (!readData.IsSuccess) return readData;
var writeData = ThingsGatewayBitConverter.ToUInt16(readData.Content, 0);
ushort mask = (ushort)(1 << mAddress.BitIndex);
ushort result = (ushort)(value[0] ? (writeData | mask) : (writeData & ~mask));
var write = ModbusHelper.GetWriteOneModbusCommand(mAddress, ThingsGatewayBitConverter.GetBytes(result));
return SendThenReturn(address, read, cancellationToken);
return SendThenReturn(mAddress, write, cancellationToken);
}
else
{
@@ -285,8 +267,15 @@ public class ModbusMaster : ProtocolBase
{
try
{
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
return await SendThenReturnAsync(address, commandResult, cancellationToken);
var mAddress = ModbusAddressHelper.ParseFrom(address, Station);
value = value.ArrayExpandToLengthEven();
byte[]? commandResult = null;
//功能码或实际长度
if (value.Length > 2 || mAddress.WriteFunction == 16)
commandResult = ModbusHelper.GetWriteModbusCommand(mAddress, value);
else
commandResult = ModbusHelper.GetWriteOneModbusCommand(mAddress, value);
return await SendThenReturnAsync(mAddress, commandResult, cancellationToken);
}
catch (Exception ex)
{
@@ -304,27 +293,27 @@ public class ModbusMaster : ProtocolBase
if (value.Length > 1 || mAddress.WriteFunction == 15)
{
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(mAddress, value, (ushort)value.Length);
return await SendThenReturnAsync(address, commandResult, cancellationToken);
return await SendThenReturnAsync(mAddress, commandResult, cancellationToken);
}
else
{
if (mAddress.BitIndex == null)
{
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(mAddress, value[0]);
return await SendThenReturnAsync(address, commandResult, cancellationToken);
return await SendThenReturnAsync(mAddress, commandResult, cancellationToken);
}
else if (mAddress.BitIndex < 16)
{
//比如40001.1
var read = ModbusHelper.GetReadModbusCommand(mAddress, 1);
var readData = await SendThenReturnAsync(address, read, cancellationToken);
var readData = await SendThenReturnAsync(mAddress, read, cancellationToken);
if (!readData.IsSuccess) return readData;
var writeData = ThingsGatewayBitConverter.ToUInt16(readData.Content, 0);
ushort mask = (ushort)(1 << mAddress.BitIndex);
ushort result = (ushort)(value[0] ? (writeData | mask) : (writeData & ~mask));
var write = ModbusHelper.GetWriteOneModbusCommand(mAddress, ThingsGatewayBitConverter.GetBytes(result));
return await SendThenReturnAsync(address, read, cancellationToken);
return await SendThenReturnAsync(mAddress, write, cancellationToken);
}
else
{
@@ -337,6 +326,32 @@ public class ModbusMaster : ProtocolBase
return new OperResult(ex);
}
}
private OperResult<byte[]> SendThenReturn(ModbusAddress mAddress, byte[] commandResult, CancellationToken cancellationToken)
{
if (Channel.ChannelType == ChannelTypeEnum.TcpService)
{
if (((TcpServiceBase)Channel).SocketClients.TryGetSocketClient($"ID={mAddress.SocketId}", out TgSocketClient? client))
return SendThenReturn(new SendMessage(commandResult), cancellationToken, client);
else
return new OperResult<byte[]>(FoundationConst.DtuNoConnectedWaining);
}
else
return SendThenReturn(new SendMessage(commandResult), cancellationToken);
}
private Task<OperResult<byte[]>> SendThenReturnAsync(ModbusAddress mAddress, byte[] commandResult, CancellationToken cancellationToken)
{
if (Channel.ChannelType == ChannelTypeEnum.TcpService)
{
if (((TcpServiceBase)Channel).SocketClients.TryGetSocketClient($"ID={mAddress.SocketId}", out TgSocketClient? client))
return SendThenReturnAsync(new SendMessage(commandResult), cancellationToken, client);
else
return Task.FromResult(new OperResult<byte[]>(FoundationConst.DtuNoConnectedWaining));
}
else
return SendThenReturnAsync(new SendMessage(commandResult), cancellationToken);
}
}
[PluginOption(Singleton = true)]

View File

@@ -264,17 +264,6 @@ public static class StringExtension
}, StringSplitOptions.RemoveEmptyEntries);
}
/// <summary>
/// 根据英文小数点进行分割字符串,去除空白的字符
/// </summary>
public static string[]? SplitStringByComma(this string? str)
{
return str?.Split(new char[1]
{
','
}, StringSplitOptions.RemoveEmptyEntries);
}
/// <summary>
/// 根据英文分号分割字符串,去除空白的字符
/// </summary>