随着社会的发展,工厂智能化的步骤越来越快,信息和数据越来越重要,MES,SCADA等智能化系统逐渐成为工厂管理的标配,于是数据采集与维护也逐渐成为了工厂工作的重点,本人把工作中学习的项目分享给大家,希望能抛砖引玉,得到大家的指点和帮助!
项目预览与概述:
预览
电脑测试预览

手机测试预览

设计思路

概述
项目分为3个部分,硬件基础(数据收集),数据采集上传,信息展示(微信小程序)
一.硬件基础 西门子S71500 PLC
1.设备选型,组态
2.数据块结构设计
3.OPC UA服务开启
4.仿真环境搭建
二.数据采集上传 OPC客户端
1.OPC UA 通信连接
2.数据采集(采用订阅方式)
3.数据处理或上传微信云数据库
三.信息展示(微信小程序)
1.选择云开发,云模板
2.修改界面
2.修改云函数和数据展示逻辑
项目实施:
一.硬件基础 西门子S71500 PLC
1.设备选型,组态

2.数据块结构设计
为了方便数据数据采集,数据块一定要设计合理,尽量模块化

3.OPC UA服务开启
西门子1500PLC配置OPC相当简单,选择好授权激活OPC服务器功能即可,具体几个容易出错的小细节见图片

尽量启用访客认证,这样方便测试

4.仿真环境搭建
我为了方便测试,电脑上安装了VM虚拟机,虚拟机里安装博途V15.1和模拟器S7-PLCSIM Advanced V3.0,如果大家也和我一样那么要注意IP设置,否则OPC客户端在本机无法采集数据,详细见下图

仿真时一定要选上支持仿真的功能,许可证类型要选上

二.数据采集上传 OPC客户端
1.OPC UA 通信连接
1.引用OPC官方的Opc.Ua.Client.dll,Opc.Ua.Core.dll
2.引用开源的OPCHelper(编译后引用)

网上搜索的帮助类,自己修改了一下,具体代码如图
OPC UA详细使用方法见下面这位大牛的文章
C#使用OpcUaHelper开源库开发客户端实现读取、订阅OPC UA服务器节点信息_牛奶咖啡13的博客-CSDN博客_c# opc ua客户端
//声明对象
private OpcHleperTest opctest;
//实例化对象
opctest = new OpcHleperTest();
//连接
try
{
//判断连接状态后连接
if (!opctest.ConnectStatus)
{
//注意连接地址
opctest.OpenConnectOfAnonymous("opc.tcp://192.168.40.8:4840");
}
//判断连接状态后按钮改变颜色
if (opctest.ConnectStatus)
{
button1.BackColor = Color.YellowGreen;
button1.Text = "已连接";
label9.BackColor = Color.Yellow;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
2.数据采集(采用订阅方式),数据处理
订阅
//定义需要订阅的数据
string str1 = "ns=3;s=\"opctest\".\"station1\".\"equipment\".\"speed\"";
string str2 = "ns=3;s=\"opctest\".\"station1\".\"equipment\".\"state\"";
string str3 = "ns=3;s=\"opctest\".\"station1\".\"operator\".\"name\"";
string str4 = "ns=3;s=\"opctest\".\"station1\".\"operator\".\"state\"";
//加入数组
string[] nolds = new string[] { str1, str2 , str3 , str4 };
//批量订阅
opctest.BatchNodeIdDatasSubscription("monitor", nolds, SubCallback);
3.数据处理或上传微信云数据库
//数据订阅的回调函数
private void SubCallback(string key, MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs args)
{
if (InvokeRequired)
{
Invoke(new Action<string, MonitoredItem, MonitoredItemNotificationEventArgs>(SubCallback), key, monitoredItem, args);
return;
}
// 如果有多个的订阅值都关联了当前的方法,可以通过key和monitoredItem来区分
MonitoredItemNotification notification = args.NotificationValue as MonitoredItemNotification;
if (notification != null)
{
//运行速度
if(monitoredItem.StartNodeId.ToString()== "ns=3;s=\"opctest\".\"station1\".\"equipment\".\"speed\"")
{
//取出订阅返回的值
textBox2.Text = notification.Value.WrappedValue.Value.ToString();
//数据上传到微信云数据库
UpData("Speed_Station1", textBox2.Text);
}
//设备状态
if (monitoredItem.StartNodeId.ToString() == "ns=3;s=\"opctest\".\"station1\".\"equipment\".\"state\"")
{
//判断取回的值
if(notification.Value.WrappedValue.Value.ToString()=="0")
{
//改变标签的颜色
label6.BackColor = Color.Yellow;
label5.BackColor = Control.DefaultBackColor;
label7.BackColor = Control.DefaultBackColor;
//数据上传到微信云数据库
UpData("State_Station1", "0");
}
if (notification.Value.WrappedValue.Value.ToString() == "1")
{
label5.BackColor = Color.YellowGreen;
label6.BackColor = Control.DefaultBackColor;
label7.BackColor = Control.DefaultBackColor;
UpData("State_Station1", "1");
}
if (notification.Value.WrappedValue.Value.ToString() == "3")
{
label7.BackColor = Color.Red;
label5.BackColor = Control.DefaultBackColor;
label6.BackColor = Control.DefaultBackColor;
UpData("State_Station1", "3");
}
}
//工号
if (monitoredItem.StartNodeId.ToString() == "ns=3;s=\"opctest\".\"station1\".\"operator\".\"name\"")
{
textBox1.Text = notification.Value.WrappedValue.Value.ToString();
UpData("Code_OP", textBox1.Text);
}
//人员状态
if (monitoredItem.StartNodeId.ToString() == "ns=3;s=\"opctest\".\"station1\".\"operator\".\"state\"")
{
if (notification.Value.WrappedValue.Value.ToString()== "True")
{
label3.BackColor = Color.YellowGreen;
label8.BackColor = Control.DefaultBackColor;
UpData("State_OP", "true");
}
if (notification.Value.WrappedValue.Value.ToString() == "False")
{
label8.BackColor = Color.Red;
label3.BackColor = Control.DefaultBackColor;
UpData("State_OP", "false");
}
}
}
}
3.微信云数据库修改
获取修改数据库的官方获取测试地址
// 通过Get请求获取access_token
HttpWebResponse httpWebResponse = GetRequest("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="你的appid"&secret="你的secret"");
string resultJson = HttpWebResponseToString(httpWebResponse);
var resultObj = DesAnonymousType(resultJson, new { access_token = "", expires_in = "" });
return resultObj.access_token;
修改微信云数据库
string accessToken = GetAccessToken();
string accessTokenqueryString = $"{{\"env\":\"你的环境名称\", \"query\": \"db.collection(\\\"你的数据库名\\\").doc(\\\"你的数据库ID\\\")" + ".update({data:" + "{" + name + ":" + value + "}})\"}";
HttpWebResponse httpWebResponse = PostRequest("https://api.weixin.qq.com/tcb/databaseupdate?access_token=" + accessToken, accessTokenqueryString);
string data = HttpWebResponseToString(httpWebResponse);
return data;
全部代码
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Opc.Ua;
using Opc.Ua.Client;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SimChart
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//实例化对象
opctest = new OpcHleperTest();
}
//声明对象
private OpcHleperTest opctest;
private void Button1_Click(object sender, EventArgs e)
{
//云数据库的accessToken
accessToken = GetAccessToken();
try
{
//判断连接状态后连接
if (!opctest.ConnectStatus)
{
opctest.OpenConnectOfAnonymous("opc.tcp://192.168.40.8:4840");
}
//判断连接状态后按钮改变颜色
if (opctest.ConnectStatus)
{
button1.BackColor = Color.YellowGreen;
button1.Text = "已连接";
label9.BackColor = Color.Yellow;
}
string str1 = "ns=3;s=\"opctest\".\"station1\".\"equipment\".\"speed\"";
string str2 = "ns=3;s=\"opctest\".\"station1\".\"equipment\".\"state\"";
string str3 = "ns=3;s=\"opctest\".\"station1\".\"operator\".\"name\"";
string str4 = "ns=3;s=\"opctest\".\"station1\".\"operator\".\"state\"";
string[] nolds = new string[] { str1, str2 , str3 , str4 };
opctest.BatchNodeIdDatasSubscription("monitor", nolds, SubCallback);
//opctest.SingleNodeIdDatasSubscription("opctest888", str1, SubCallback2);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
//单节点数据订阅的回调函数
private void SubCallback2(string key, MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs args)
{
if (InvokeRequired)
{
Invoke(new Action<string, MonitoredItem, MonitoredItemNotificationEventArgs>(SubCallback2), key, monitoredItem, args);
return;
}
// 如果有多个的订阅值都关联了当前的方法,可以通过key和monitoredItem来区分
MonitoredItemNotification notification = args.NotificationValue as MonitoredItemNotification;
if (notification != null)
{
textBox1.Text = notification.Value.WrappedValue.Value.ToString();
}
}
//数据订阅的回调函数
private void SubCallback(string key, MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs args)
{
if (InvokeRequired)
{
Invoke(new Action<string, MonitoredItem, MonitoredItemNotificationEventArgs>(SubCallback), key, monitoredItem, args);
return;
}
// 如果有多个的订阅值都关联了当前的方法,可以通过key和monitoredItem来区分
MonitoredItemNotification notification = args.NotificationValue as MonitoredItemNotification;
if (notification != null)
{
//运行速度
if(monitoredItem.StartNodeId.ToString()== "ns=3;s=\"opctest\".\"station1\".\"equipment\".\"speed\"")
{
textBox2.Text = notification.Value.WrappedValue.Value.ToString();
UpData("Speed_Station1", textBox2.Text);
}
//设备状态
if (monitoredItem.StartNodeId.ToString() == "ns=3;s=\"opctest\".\"station1\".\"equipment\".\"state\"")
{
if(notification.Value.WrappedValue.Value.ToString()=="0")
{
label6.BackColor = Color.Yellow;
label5.BackColor = Control.DefaultBackColor;
label7.BackColor = Control.DefaultBackColor;
UpData("State_Station1", "0");
}
if (notification.Value.WrappedValue.Value.ToString() == "1")
{
label5.BackColor = Color.YellowGreen;
label6.BackColor = Control.DefaultBackColor;
label7.BackColor = Control.DefaultBackColor;
UpData("State_Station1", "1");
}
if (notification.Value.WrappedValue.Value.ToString() == "3")
{
label7.BackColor = Color.Red;
label5.BackColor = Control.DefaultBackColor;
label6.BackColor = Control.DefaultBackColor;
UpData("State_Station1", "3");
}
}
//工号
if (monitoredItem.StartNodeId.ToString() == "ns=3;s=\"opctest\".\"station1\".\"operator\".\"name\"")
{
textBox1.Text = notification.Value.WrappedValue.Value.ToString();
UpData("Code_OP", textBox1.Text);
}
//人员状态
if (monitoredItem.StartNodeId.ToString() == "ns=3;s=\"opctest\".\"station1\".\"operator\".\"state\"")
{
if (notification.Value.WrappedValue.Value.ToString()== "True")
{
label3.BackColor = Color.YellowGreen;
label8.BackColor = Control.DefaultBackColor;
UpData("State_OP", "true");
}
if (notification.Value.WrappedValue.Value.ToString() == "False")
{
label8.BackColor = Color.Red;
label3.BackColor = Control.DefaultBackColor;
UpData("State_OP", "false");
}
}
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
//关闭连接
opctest.CloseConnect();
}
///云数据库相关
///
/// <summary>
/// 发送http Get请求
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static HttpWebResponse GetRequest(string url)
{
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "GET";
request.ContentType = "application/x-www-form-urlencoded";//链接类型
return request.GetResponse() as HttpWebResponse;
}
/// <summary>
/// 发送http Post请求
/// </summary>
/// <returns></returns>
public static HttpWebResponse PostRequest(string url, string messsage)
{
byte[] byteData = Encoding.UTF8.GetBytes(messsage);
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Method = "POST";
webRequest.ContentType = "application/json;charset=UTF-8";
webRequest.ContentLength = byteData.Length;
using (Stream stream = webRequest.GetRequestStream())
{
stream.Write(byteData, 0, byteData.Length);
}
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
return response;
}
/// <summary>
/// 从HttpWebResponse对象中提取响应的数据转换为字符串
/// </summary>
/// <param name="webresponse"></param>
/// <returns></returns>
public static string HttpWebResponseToString(HttpWebResponse webresponse)
{
using (Stream s = webresponse.GetResponseStream())
{
StreamReader reader = new StreamReader(s, Encoding.UTF8);
return reader.ReadToEnd();
}
}
/// <summary>
/// Json字符串转为匿名对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="json"></param>
/// <param name="anonymousTypeObject"></param>
/// <returns></returns>
public static T DesAnonymousType<T>(string json, T anonymousTypeObject)
{
return JsonConvert.DeserializeAnonymousType(json, anonymousTypeObject);
}
/// <summary>
/// 获取access_token
/// </summary>
/// <returns></returns>
public static string GetAccessToken()
{
// 通过Get请求获取access_token
HttpWebResponse httpWebResponse = GetRequest("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx017496cac8389210&secret=51c000bc2fa6d32fa8969e9cb8f71072");
string resultJson = HttpWebResponseToString(httpWebResponse);
var resultObj = DesAnonymousType(resultJson, new { access_token = "", expires_in = "" });
return resultObj.access_token;
}
/// <summary>
/// 查询数据
/// </summary>
/// <param name="queryString">形如: $"{{\"env\":\"小程序环境id\", \"query\": \"db.collecti(\\\"数据集合名称\\\").where({{集合中字段:\\\"集合中字段值\\\"}}).limit(10).get()\"}}"</param>
/// <returns></returns>
//public static string GetData(string queryString)
public static string GetData()
{
string accessToken = GetAccessToken();
string accessTokenqueryString = $"{{\"env\":\"zunzhi-5gthrfdrd0504463\", \"query\": \"db.collection(\\\"Test\\\").where({{_id:\\\"TestByClient\\\"}}).limit(10).get()\"}}";
HttpWebResponse httpWebResponse = PostRequest("https://api.weixin.qq.com/tcb/databasequery?access_token=" + accessToken, accessTokenqueryString);
string data = HttpWebResponseToString(httpWebResponse);
return data;
}
static string accessToken = "";
/// <summary>
/// 更新数据
/// </summary>
/// <param name="queryString">形如: $"{{\"env\":\"小程序环境id\", \"query\": \"db.collecti(\\\"数据集合名称\\\").where({{集合中字段:\\\"集合中字段值\\\"}}).update({data:{集合中字段:\\\"集合中字段值\\\"})"</param>
/// <returns></returns>
//public static string GetData(string queryString)
public static string UpData(string name, string value)
{
//string accessToken = GetAccessToken();
string accessTokenqueryString = $"{{\"env\":\"zunzhi-5gthrfdrd0504463\", \"query\": \"db.collection(\\\"Test\\\").doc(\\\"TestByClient\\\")" + ".update({data:" + "{" + name + ":" + value + "}})\"}";
HttpWebResponse httpWebResponse = PostRequest("https://api.weixin.qq.com/tcb/databaseupdate?access_token=" + accessToken, accessTokenqueryString);
string data = HttpWebResponseToString(httpWebResponse);
return data;
}
/// <summary>
/// 通过FileId获取文件下载URL
/// </summary>
/// <param name="queryString">形如:$"{{\"env\": \"环境ID\",\"file_list\": [{{\"fileid\":文件ID\",\"max_age\":7200 }}]}}"</param>
/// <returns></returns>
public static string GetDownFileUrl(string queryString)
{
string accessToken = GetAccessToken();
string url = $"https://api.weixin.qq.com/tcb/batchdownloadfile?access_token={accessToken}";
HttpWebResponse httpWebResponse = PostRequest(url, queryString);
string downFileUrl = HttpWebResponseToString(httpWebResponse);
return downFileUrl;
}
//提取数据
public static string GetValue(string JsonString, string Jsonvalue)
{
JObject JsonTemp = JObject.Parse(JsonString);
string str1 = JsonTemp["data"].ToString().Replace("[", "").Replace("]", "").Trim();
str1 = str1.Substring(1, str1.Length - 2).Replace(@"\", "");
JObject JsonTemp2 = JObject.Parse(str1);
string str2 = JsonTemp2[Jsonvalue].ToString();
return str2;
}
}
}
正常后返回的数据见下图

三.信息展示(微信小程序)
1.选择云开发,云模板
打开微信的官方开发软件,建立项目,选择云开发,选择官方的云模板

2.简单修改一下界面
把模板中不需要的注释掉,加入自己的代码
<view class="title">XX车间状态</view>
<view class="top_tip">努力工作!好好学习!</view>
<view class="app">
员工
<view class="horizontal">
<view style="color: lightsalmon;">员工工号</view>
<view style="border-style:solid; border-color: darkorchid; height: 38px;width: 200px;color: olivedrab;text-align: center;">{{record[0]["Code_OP"]}}</view>
</view>
<view class="horizontal">
<text wx:if="{{record[0]['State_OP']}}" style="border-style:solid;border-color: turquoise; background-color: rgb(30, 235, 23);">在岗</text>
<text wx:else="" style="border-style:solid;border-color: turquoise; background-color: slategray;">在岗</text>
<text wx:if="{{!record[0]['State_OP']}}"style="border-style:solid;border-color: turquoise; background-color:red;">离岗</text>
<text wx:else="" style="border-style:solid;border-color: turquoise; background-color: slategray;">离岗</text>
</view>
</view>
<view class="app">
设备
<view class="horizontal">
<view style="color: lightsalmon;">运行速度</view>
<view style="border-style:solid; border-color: darkorchid; height: 38px;width: 200px;color: olivedrab;text-align: center;">{{record[0]["Speed_Station1"]}}</view>
</view>
<view class="horizontal">
<text wx:if="{{record[0]['State_Station1']==1}}" style="border-style:solid;border-color: turquoise; background-color: rgb(30, 235, 91);">运行</text>
<text wx:else="" style="border-style:solid;border-color: turquoise; background-color: slategray;">运行</text>
<text wx:if="{{record[0]['State_Station1']==0}}" style="border-style:solid;border-color: turquoise; background-color: yellow;">停止</text>
<text wx:else="" style="border-style:solid;border-color: turquoise; background-color: slategray;">停止</text>
<text wx:if="{{record[0]['State_Station1']==3}}" style="border-style:solid;border-color: turquoise; background-color:red;">故障</text>
<text wx:else="" style="border-style:solid;border-color: turquoise; background-color: slategray;">故障</text>
</view>
</view>
<button style="border-style: solid; border-color: springgreen;background-color: palegreen;margin-top: 30px;" bindtap="getRecord">刷新</button>
3..修改云函数和数据展示逻辑
添加云数据库信息

修改微信云函数,修改后一定上传部署

在页面的js文件中加入查询云数据库的方法
//查询数据
getRecord() {
wx.showLoading({
title: '',
});
wx.cloud.callFunction({
name: 'quickstartFunctions',
config: {
env: this.data.envId
},
data: {
//注意这个命令,不要选错
type: 'selectRecord'
}
}).then((resp) => {
this.setData({
haveGetRecord: true,
record: resp.result.data
});
wx.hideLoading();
}).catch((e) => {
console.log(e);
this.setData({
showUploadTip: true
});
wx.hideLoading();
});
},

修改后保存编译,模拟器就会显示查询的结果,是不是很惊喜?整个项目比较简单,稍微有些编程知识即可入手
我在页面中加入了定时器可以实现数据实时更新,但很快就把我的数据库测试次数用完了,所以就注释掉了,大家打开注释用定时器刷新页面时一定要注意,每天的次数用完微信官方就不允许访问数据库了!页面写满了,无法再写了,代码见附件吧,我已分类打包,完整上传!最后谢谢大家的观看,大家互相交流,共同进步,谢谢!
项目资源下载
工业4.0智能工厂简单学习项目-C#文档类资源-CSDN下载
在railstutorial中,作者为什么选择使用这个(代码list10.25):http://ruby.railstutorial.org/chapters/updating-showing-and-deleting-usersnamespace:dbdodesc"Filldatabasewithsampledata"task:populate=>:environmentdoRake::Task['db:reset'].invokeUser.create!(:name=>"ExampleUser",:email=>"example@railstutorial.org",:passwo
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘
我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="
有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit
我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。
我的工作要求我为某些测试自动生成电子邮件。我一直在四处寻找,但未能找到可以快速实现的合理解决方案。它需要在outlook而不是其他邮件服务器中,因为我们有一些奇怪的身份验证规则,我们需要保存草稿而不是仅仅发送邮件的选项。显然win32ole可以做到这一点,但我找不到任何相当简单的例子。 最佳答案 假设存储了Outlook凭据并且您设置为自动登录到Outlook,WIN32OLE可以很好地完成此操作:require'win32ole'outlook=WIN32OLE.new('Outlook.Application')message=
//1.验证返回状态码是否是200pm.test("Statuscodeis200",function(){pm.response.to.have.status(200);});//2.验证返回body内是否含有某个值pm.test("Bodymatchesstring",function(){pm.expect(pm.response.text()).to.include("string_you_want_to_search");});//3.验证某个返回值是否是100pm.test("Yourtestname",function(){varjsonData=pm.response.json