SharpNodeSettings项目,可配置的数据采集,统一的工业数据网关,OPC UA服务器开发,PLC数据发布到自身内存,redis,opc ua,以及数据可视化开发
本项目隶属于 HslCommunication 项目的SDK套件,如果不清楚HslCommunication组件的话,可以先了解那个项目,源代码地址:https://github.com/dathlin/HslCommunication
联系作者及加群方式(激活码在群里发放):http://www.hslcommunication.cn/Cooperation
本项目源代码地址:https://github.com/dathlin/SharpNodeSettings
本项目的主要实现的功能主要有2个:
- 实现单个设备信息的可配置,可存储,采用一个相对标准的Xml存储机制实现,适用的场景是:如果你有20个西门子PLC(种类需要一致),但是PLC的ip地址不一致,或是具体的型号不一致,需要进行可视化的存储
- 实现一个数据网关中心,内置了一个自身协议的网络,当然您也可以实现其他的,比如示例项目里的Redis数据网关,OPC UA数据网关。
本项目的所有的核心构建,都是围绕一定格式的Xml文件展开的,以 NodeClass 作为节点的基类,赋予每个节点 Name 值,Description 值,节点下可以跟随子节点,或是跟随设备节点,设备下可以跟随请求节点,多说无益,直接上代码
<?xml version="1.0" encoding="utf-8"?>
<Settings>
<NodeClass Name="Devices" Description="所有的设备的集合对象">
<NodeClass Name="分厂一" Description="">
<NodeClass Name="车间一" Description="" />
<NodeClass Name="车间二" Description="">
<DeviceNode Name="ModbusTcp客户端" Description="这是描述" DeviceType="10" ConnectTimeOut="1000" CreateTime="2018/8/9 19:58:49" InstallationDate="2018/8/9 19:58:49" IpAddress="127.0.0.1" Port="502" Station="1" IsAddressStartWithZero="true" IsWordReverse="false" IsStringReverse="false">
<DeviceRequest Name="数据请求" Description="一次完整的数据请求" Address="0" Length="30" CaptureInterval="1000" PraseRegularCode="ABCD" />
</DeviceNode>
</NodeClass>
</NodeClass>
<NodeClass Name="分厂二" Description="位于西南方">
<NodeClass Name="车间三" Description="">
<DeviceNode Name="测试设备二" Description="这是测试设备二的描述" DeviceType="10" ConnectTimeOut="1000" CreateTime="2018/8/10 23:01:28" InstallationDate="2018/8/10 23:01:28" IpAddress="127.0.0.1" Port="502" Station="1" IsAddressStartWithZero="true" IsWordReverse="false" IsStringReverse="false">
<DeviceRequest Name="数据请求" Description="一次完整的数据请求" Address="100" Length="10" CaptureInterval="500" PraseRegularCode="B" />
</DeviceNode>
</NodeClass>
</NodeClass>
</NodeClass>
<NodeClass Name="Server" Description="所有挂载的服务器">
<ServerNode Name="异形服务器" Description="这是一个异形服务器" CreateTime="2018/8/8 13:29:30" Port="1234" ServerType="2" Password="" />
</NodeClass>
<NodeClass Name="Regular" Description="所有的解析规则的信息">
<RegularNode Name="ABCD" Description="">
<RegularItemNode Name="温度" Description="" Index="0" TypeCode="3" TypeLength="1" />
<RegularItemNode Name="风俗" Description="" Index="2" TypeCode="9" TypeLength="1" />
<RegularItemNode Name="转速" Description="" Index="14" TypeCode="9" TypeLength="1" />
<RegularItemNode Name="机器人关节" Description="" Index="18" TypeCode="9" TypeLength="6" />
<RegularItemNode Name="cvsdf" Description="" Index="42" TypeCode="9" TypeLength="1" />
<RegularItemNode Name="条码" Description="条码信息" Index="6" TypeCode="11" TypeLength="8" />
<RegularItemNode Name="开关量" Description="设备的开关量信息" Index="368" TypeCode="1" TypeLength="8" />
</RegularNode>
<RegularNode Name="B" Description="">
<RegularItemNode Name="温度" Description="" Index="0" TypeCode="3" TypeLength="1" />
<RegularItemNode Name="压力" Description="" Index="2" TypeCode="3" TypeLength="1" />
</RegularNode>
</NodeClass>
</Settings>
以上就是一个示例的XML文件,手动创建这样的一个数据表将会是难以想象的,所以本组件提供了可视化的数据创建中心,
Form nodeSettings = new SharpNodeSettings.View.FormNodeSetting( "settings.xml" )
nodeSettings.ShowDialog();
这样就可以显示一个窗体,显示节点配置信息了。
不仅可以配置左侧的节点,设备信息,还支持配置解析规则和可视化的显示,辅助你找到正确的字节索引。点击保存,即可生成上述示例的一个xml配置表。
我们有了这个配置文件后,如何才能解析出来,并且生成相应的设备呢?
我们可以调用 SharpNodeServer 来创建服务器应用,可以生成相应的节点信息,并且根据配置信息来请求设备,更新对应的数据。创建服务器的代码如下:
SharpNodeServer sharpNodeServer = new SharpNodeServer( );
sharpNodeServer.LoadByXmlFile( "settings.xml" );
sharpNodeServer.ServerStart( 12345 );
这样就启动了一个最简单的服务器,主要包含实例化,加载配置,启动服务器,注意:加载配置必须放置到服务器启动之前。
怎样查看服务器的数据呢?内置了一个默认的 SimplifyNet 服务器,想要知道更多的这个服务器的内容,可以参照下面的博客:https://www.cnblogs.com/dathlin/p/7697782.html
基于 NetSimplifyClient 实现了一个通用的数据节点查看器,需要指定服务器的Ip地址和端口号:
SharpNodeSettings.View.FormNodeView form = new SharpNodeSettings.View.FormNodeView( "127.0.0.1",12345 );
form.ShowDialog();
如果你想实现访问单个的数据,可以使用 NetSimplifyClient 创建的Demo来访问,需要注意的是,此处请求的数据都是序列化的JSON字符串。
在实际开发中,可能你不需要上述的配置功能,你就想实现某个PLC的设备信息是可配置的,那么也可以通过本组件实现:
SharpNodeSettings.View.FormSelectDevice selectDevice = new View.FormSelectDevice( );
if (selectDevice.ShowDialog( ) == DialogResult.OK)
{
XElement xmlDevice = selectDevice.DeviceXml;
// 设备的配置对象可用于存储,网络传输等等操作 // 如果想要通过xml信息创建设备
SharpNodeSettings.Device.DeviceCore deviceCore = SharpNodeSettings.Util.CreateFromXElement( xmlDevice );
// 演示读取数据,此处有个问题在于如果是相同种类的PLC,应用还是很方便的,如果是不同种类的,地址模型就比较麻烦。
HslCommunication.OperateResult<short> read = deviceCore.ReadWriteDevice.ReadInt16( "D100" );
}
Quick Start
按照如下的步骤走,就可以急速体验本项目所传达的核心功能价值,就可以明白本项目是否符合您的需求。启动测试之前,你需要准备个真实的设备:
- 西门子PLC
- 三菱PLC
- 欧姆龙PLC
- ModbusTcp设备
如果您没有真实的设备,也可以从网上下载个Modbus服务器软件,这里也提供一个下载地址:ModbusTcpServer.zip
下载完成后启动服务器即可。
配置Xml信息
去本项目的目录下配置设备的信息: \SharpNodeSettings\XmlFile 运行 SharpNodeSettings.Tools.exe 进行配置,已经配置了一部分,如果想要快速开始,忽略本步骤也可以。
SampleServer
本示例直接重新生成 SampleServer 项目,启动程序即可。如果想要看实际的数据信息,启动 SharpNodeSettings.NodeView项目查看
RedisServer
本示例是在 SampleServer 的基础上添加了Redis服务器,所以需要先安装好Redis服务器,windows版本下载地址:https://github.com/MicrosoftArchive/redis/releases
当然,最好再下载安装一个redis服务器的可视化工具,此处推荐 RedisDesktopManagerhttps://github.com/uglide/RedisDesktopManager/releases
然后基于本项目,重新生成 SharpNodeSettings.RedisServer 项目,启动服务器
上述的 SharpNodeSettings.NodeView 项目依然可以查看,然后下图演示Redis
OpcUaServer
本示例是演示从PLC采集数据并且写入到OPC UA服务器中的示例,重新生成 SharpNodeSettings.OpcUaServer 项目,启动它,如果显示是否增加信任证书时,选择是即可。
首先创建OPC UA服务器项目的时候,需要根据xml文件创建对应的OPC UA节点,这部分还是比较麻烦的
#region INodeManager Members
/// <summary>
/// Does any initialization required before the address space can be used.
/// </summary>
/// <remarks>
/// The externalReferences is an out parameter that allows the node manager to link to nodes
/// in other node managers. For example, the 'Objects' node is managed by the CoreNodeManager and
/// should have a reference to the root folder node(s) exposed by this node manager.
/// </remarks>
public override void CreateAddressSpace( IDictionary<NodeId, IList<IReference>> externalReferences )
{
lock (Lock)
{
LoadPredefinedNodes( SystemContext, externalReferences ); IList<IReference> references = null; if (!externalReferences.TryGetValue( ObjectIds.ObjectsFolder, out references ))
{
externalReferences[ObjectIds.ObjectsFolder] = references = new List<IReference>( );
} dict_BaseDataVariableState = new Dictionary<string, BaseDataVariableState>( );
try
{
// =========================================================================================
//
// 此处需要加载本地文件,并且创建对应的节点信息,
//
// =========================================================================================
sharpNodeServer = new SharpNodeServer( );
sharpNodeServer.WriteCustomerData = ( Device.DeviceCore deviceCore, string name ) =>
{
string opcNode = "ns=2;s=" + string.Join( "/", deviceCore.DeviceNodes ) + "/" + name;
lock (Lock)
{
if (dict_BaseDataVariableState.ContainsKey( opcNode ))
{
dict_BaseDataVariableState[opcNode].Value = deviceCore.GetDynamicValueByName( name );
dict_BaseDataVariableState[opcNode].ClearChangeMasks( SystemContext, false );
}
}
}; XElement element = XElement.Load( "settings.xml" );
dicRegularItemNode = SharpNodeSettings.Util.ParesRegular( element ); AddNodeClass( null, element, references ); // 加载配置文件之前设置写入方法 sharpNodeServer.LoadByXmlFile( "settings.xml" );
// 最后再启动服务器信息
sharpNodeServer.ServerStart( 12345 );
}
catch (Exception e)
{
Utils.Trace( e, "Error creating the address space." );
}
}
} private void AddNodeClass( NodeState parent, XElement nodeClass, IList<IReference> references )
{
foreach (var xmlNode in nodeClass.Elements( ))
{
if (xmlNode.Name == "NodeClass")
{
SharpNodeSettings.Node.NodeBase.NodeClass nClass = new SharpNodeSettings.Node.NodeBase.NodeClass( );
nClass.LoadByXmlElement( xmlNode ); FolderState son;
if (parent == null)
{
son = CreateFolder( null, nClass.Name );
son.Description = nClass.Description;
son.AddReference( ReferenceTypes.Organizes, true, ObjectIds.ObjectsFolder );
references.Add( new NodeStateReference( ReferenceTypes.Organizes, false, son.NodeId ) );
son.EventNotifier = EventNotifiers.SubscribeToEvents;
AddRootNotifier( son ); AddNodeClass( son, xmlNode, references );
AddPredefinedNode( SystemContext, son );
}
else
{
son = CreateFolder( parent, nClass.Name, nClass.Description );
AddNodeClass( son, xmlNode, references );
}
}
else if (xmlNode.Name == "DeviceNode")
{
AddDeviceCore( parent, xmlNode );
}
else if (xmlNode.Name == "Server")
{
AddServer( parent, xmlNode, references );
}
}
} private void AddDeviceCore( NodeState parent, XElement device )
{
if (device.Name == "DeviceNode")
{
// 提取名称和描述信息
string name = device.Attribute( "Name" ).Value;
string description = device.Attribute( "Description" ).Value; // 创建OPC节点
FolderState deviceFolder = CreateFolder( parent, device.Attribute( "Name" ).Value, device.Attribute( "Description" ).Value );
// 添加Request
foreach (var requestXml in device.Elements( "DeviceRequest" ))
{
DeviceRequest deviceRequest = new DeviceRequest( );
deviceRequest.LoadByXmlElement( requestXml ); AddDeviceRequest( deviceFolder, deviceRequest );
}
}
} private void AddServer( NodeState parent, XElement xmlNode, IList<IReference> references )
{
int serverType = int.Parse( xmlNode.Attribute( "ServerType" ).Value );
if (serverType == ServerNode.ModbusServer)
{
NodeModbusServer serverNode = new NodeModbusServer( );
serverNode.LoadByXmlElement( xmlNode ); FolderState son = CreateFolder( parent, serverNode.Name, serverNode.Description );
AddNodeClass( son, xmlNode, references );
}
else if (serverType == ServerNode.AlienServer)
{
AlienServerNode alienNode = new AlienServerNode( );
alienNode.LoadByXmlElement( xmlNode ); FolderState son = CreateFolder( parent, alienNode.Name, alienNode.Description );
AddNodeClass( son, xmlNode, references );
}
}
private void AddDeviceRequest( NodeState parent, DeviceRequest deviceRequest )
{
// 提炼真正的数据节点
if (!dicRegularItemNode.ContainsKey( deviceRequest.PraseRegularCode )) return;
List<RegularItemNode> regularNodes = dicRegularItemNode[deviceRequest.PraseRegularCode]; foreach (var regularNode in regularNodes)
{
if (regularNode.RegularCode == RegularNodeTypeItem.Bool.Code)
{
if (regularNode.TypeLength == 1)
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.Boolean, ValueRanks.Scalar, default( bool ) );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
else
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.Boolean, ValueRanks.OneDimension, new bool[regularNode.TypeLength] );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
}
else if (regularNode.RegularCode == RegularNodeTypeItem.Byte.Code)
{
if (regularNode.TypeLength == 1)
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.Byte, ValueRanks.Scalar, default( byte ) );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
else
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.Byte, ValueRanks.OneDimension, new byte[regularNode.TypeLength] );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
}
else if (regularNode.RegularCode == RegularNodeTypeItem.Int16.Code)
{
if (regularNode.TypeLength == 1)
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.Int16, ValueRanks.Scalar, default( short ) );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
else
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.Int16, ValueRanks.OneDimension, new short[regularNode.TypeLength] );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
}
else if (regularNode.RegularCode == RegularNodeTypeItem.UInt16.Code)
{
if (regularNode.TypeLength == 1)
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.UInt16, ValueRanks.Scalar, default( ushort ) );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
else
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.UInt16, ValueRanks.OneDimension, new ushort[regularNode.TypeLength] );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
}
else if (regularNode.RegularCode == RegularNodeTypeItem.Int32.Code)
{
if (regularNode.TypeLength == 1)
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.Int32, ValueRanks.Scalar, default( int ) );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
else
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.Int32, ValueRanks.OneDimension, new int[regularNode.TypeLength] );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
}
else if (regularNode.RegularCode == RegularNodeTypeItem.UInt32.Code)
{
if (regularNode.TypeLength == 1)
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.UInt32, ValueRanks.Scalar, default( uint ) );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
else
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.UInt32, ValueRanks.OneDimension, new uint[regularNode.TypeLength] );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
}
else if (regularNode.RegularCode == RegularNodeTypeItem.Float.Code)
{
if (regularNode.TypeLength == 1)
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.Float, ValueRanks.Scalar, default( float ) );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
else
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.Float, ValueRanks.OneDimension, new float[regularNode.TypeLength] );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
}
else if (regularNode.RegularCode == RegularNodeTypeItem.Int64.Code)
{
if (regularNode.TypeLength == 1)
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.Int64, ValueRanks.Scalar, default( long ) );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
else
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.Int64, ValueRanks.OneDimension, new long[regularNode.TypeLength] );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
}
else if (regularNode.RegularCode == RegularNodeTypeItem.UInt64.Code)
{
if (regularNode.TypeLength == 1)
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.UInt64, ValueRanks.Scalar, default( ulong ) );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
else
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.UInt64, ValueRanks.OneDimension, new ulong[regularNode.TypeLength] );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
}
else if (regularNode.RegularCode == RegularNodeTypeItem.Double.Code)
{
if (regularNode.TypeLength == 1)
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.Double, ValueRanks.Scalar, default( double ) );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
else
{
var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.Double, ValueRanks.OneDimension, new double[regularNode.TypeLength] );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
}
else if (regularNode.RegularCode == RegularNodeTypeItem.StringAscii.Code ||
regularNode.RegularCode == RegularNodeTypeItem.StringUnicode.Code ||
regularNode.RegularCode == RegularNodeTypeItem.StringUtf8.Code)
{ var dataVariableState = CreateBaseVariable( parent, regularNode.Name, regularNode.Description, DataTypeIds.String, ValueRanks.OneDimension, string.Empty );
dict_BaseDataVariableState.Add( dataVariableState.NodeId.ToString( ), dataVariableState );
}
} } /// <summary>
/// 创建一个新的节点,节点名称为字符串
/// </summary>
protected FolderState CreateFolder( NodeState parent, string name )
{
return CreateFolder( parent, name, string.Empty );
} /// <summary>
/// 创建一个新的节点,节点名称为字符串
/// </summary>
protected FolderState CreateFolder( NodeState parent, string name, string description )
{
FolderState folder = new FolderState( parent ); folder.SymbolicName = name;
folder.ReferenceTypeId = ReferenceTypes.Organizes;
folder.TypeDefinitionId = ObjectTypeIds.FolderType;
folder.Description = description;
if (parent == null)
{
folder.NodeId = new NodeId( name, NamespaceIndex );
}
else
{
folder.NodeId = new NodeId( parent.NodeId.ToString( ) + "/" + name );
}
folder.BrowseName = new QualifiedName( name, NamespaceIndex );
folder.DisplayName = new LocalizedText( name );
folder.WriteMask = AttributeWriteMask.None;
folder.UserWriteMask = AttributeWriteMask.None;
folder.EventNotifier = EventNotifiers.None; if (parent != null)
{
parent.AddChild( folder );
} return folder;
} /// <summary>
/// 创建一个值节点,类型需要在创建的时候指定
/// </summary>
protected BaseDataVariableState CreateBaseVariable( NodeState parent, string name, string description, NodeId dataType, int valueRank, object defaultValue )
{
BaseDataVariableState variable = new BaseDataVariableState( parent ); variable.SymbolicName = name;
variable.ReferenceTypeId = ReferenceTypes.Organizes;
variable.TypeDefinitionId = VariableTypeIds.BaseDataVariableType;
if (parent == null)
{
variable.NodeId = new NodeId( name, NamespaceIndex );
}
else
{
variable.NodeId = new NodeId( parent.NodeId.ToString( ) + "/" + name );
}
variable.Description = description;
variable.BrowseName = new QualifiedName( name, NamespaceIndex );
variable.DisplayName = new LocalizedText( name );
variable.WriteMask = AttributeWriteMask.DisplayName | AttributeWriteMask.Description;
variable.UserWriteMask = AttributeWriteMask.DisplayName | AttributeWriteMask.Description;
variable.DataType = dataType;
variable.ValueRank = valueRank;
variable.AccessLevel = AccessLevels.CurrentReadOrWrite;
variable.UserAccessLevel = AccessLevels.CurrentReadOrWrite;
variable.Historizing = false;
variable.Value = defaultValue;
variable.StatusCode = StatusCodes.Good;
variable.Timestamp = DateTime.Now; if (parent != null)
{
parent.AddChild( variable );
} return variable;
}
然后再启动一个 OPC UA Client的示例项目
SharpNodeSettings项目,可配置的数据采集,统一的工业数据网关,OPC UA服务器开发,PLC数据发布到自身内存,redis,opc ua,以及数据可视化开发的更多相关文章
- Maven学习笔记-02-Maven项目打包配置与测试
一 Maven项目打包配置 1 为整个项目统一指定字符集 <properties> <project.build.sourceEncoding>UTF-</project ...
- 如何为你的 Vue 项目添加配置 Stylelint
如何为你的 Vue 项目添加配置 Stylelint 现在已经是 9102 年了,网上许多教程和分享帖都已经过期,照着他们的步骤来会踩一些坑,如 stylelint-processor-html 已经 ...
- Android项目实战之高仿网易云音乐创建项目和配置
这一节我们来讲解创建项目:说道大家可能就会说了,创建项目还有谁不会啊,还需要讲吗,别急听我慢慢到来,肯定有你不知道的. 使用项目Android Studio创建项目我们这里就不讲解了,主要是讲解如何配 ...
- Taurus.MVC 微服务框架 入门开发教程:项目集成:5、统一的日志管理。
系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...
- 如何结合整洁架构和MVP模式提升前端开发体验(三) - 项目工程化配置、规范篇
工程化配置 还是开发体验的问题,跟开发体验有关的项目配置无非就是使用 eslint.prettier.stylelint 统一代码风格. formatting and lint eslint.pret ...
- android项目中配置NDK自动编译生成so文件
1 下载ndk开发包 2 在android 项目中配置编译器(以HelloJni项目为例) 2.1 创建builer (a)Project->Properties->Builder ...
- 配置安装CocoPods后进行 项目基本配置
配置安装CocoPods后进行 项目基本配置总结 1)终端在文件根目录下输入 $ touch Podfile 创建一个空白的Podfile文件 2)然后在使用编辑器打开Podfile文件进行需要配置的 ...
- VS项目属性配置实验过程
(原创,转载注明出处:http://www.cnblogs.com/binxindoudou/p/4017975.html ) 一.实验背景 cocos2d-x已经发展的相对完善了,从项目的创建.编译 ...
- ckeditor编辑器在java项目中配置
一.基本使用: 1.所需文件架包 A. Ckeditor基本文件包,比如:ckeditor_3.6.2.zip 下载地址:http://ckeditor.com/download 2.配置使用 A.将 ...
随机推荐
- mac终端显示日历信息命令
cal 命令: 用法: usage: cal [-jy] [[month] year] cal [-j] [-m month] [year] ncal [-Jjpwy] [-s country_cod ...
- 跟我学Makefile(三)
紧接着跟我学Makefile(二)继续学习:变量高级用法 (1)变量值的替换 :替换变量中的共有的部分,其格式是“$(var:a=b)”或是“${var:a=b}”,把变量“var”中所有以“a”字串 ...
- iOS error: -34018
一般报这个错误是由于操作keychain 报的错. 遇到该情况的情况: 1.是否打开权限 2.苹果自身的bug,传送门:https://stackoverflow.com/questions/2974 ...
- WebStorm下使用TypeScript
TypeScript也可使用Visual Studio 进行开发 TypeScript官网地址:(http://www.typescriptlang.org/) 1.先安装WebStorm WebSt ...
- extern--C#调用C++等其他非托管代码
例如: https://www.cnblogs.com/sosoft/p/extern.html
- 20145105 《Java程序设计》实验一总结
实验一 Java开发环境的熟悉 一. 实验内容: (一)使用JDK编译.运行简单的程序 (二)使用idea编辑.编译.运行.调试Java程序. 二. 实验步骤: (一) 命令行下J ...
- SDN前瞻 网络的前世今生
本文基于SDN导论的视频而成:SDN导论 目前网络层面流行的技术概念:虚拟中心:公有云私有云:数据中心等等. SDN主要的模拟器:Mininet OpenDaylight(Cisco) ONOS(AT ...
- POJ 2115 C Looooops(模线性方程)
http://poj.org/problem?id=2115 题意: 给你一个变量,变量初始值a,终止值b,每循环一遍加c,问一共循环几遍终止,结果mod2^k.如果无法终止则输出FOREVER. 思 ...
- Yii框架(二)Model处理数据
熟悉php的autoload机制,自己实现一个autoload函数 一.复习框架: basic/ 应用根目录 composer.json Composer 配置文件, 描述包信息 config/ 包含 ...
- BZOJ 3122 【SDOI2013】 随机数生成器
题目链接:随机数生成器 经典数学题…… 为了方便接下来的处理,我们可以先把\(X_1=t\)的情况特判掉. 当\(a=0\)的时候显然只需再判一下\(b\)是否等于\(t\)即可. 当\(a=1\)的 ...