2019年11月13日

Thingworx中的SPC计算工具


Thingworx提供了两个SPC计算工具: StatisticalCalculationThingShape,和StatisticalMonitoringThingShape,但前提条件是需要DescriptiveAnalytics平台。
大致步骤是:
1. 购买安装DescriptiveAnalytics。
2. 建立Thing,配置Data Shape和Value Stream,设置数值属性logged。
3. 调用StatisticalCalculationThingShape.QueryTimedValuesForProperty生成TimedValues类型的InfoTable。
4. 调用StatisticalCalculationThingShape.CalculateMeanValue等函数生成具体的SPC数值。
这种方式适用于工艺参数采集,即Thing和Property均已事先定义的情况下。

如果不采用DescriptiveAnalytics平台,或者说数据不适合用Value Stream记录(比如直接从拧紧枪数据库查询数据),那么就需要自己计算SPC了。
具体步骤为:
1. 经由数据库查询等方式生成InfoTable类型的数据集。
2. 调用infotable for loop方法,逐行读取并分析数据,计算最大值、最小值、平均值。
3. 利用数组暂存数据,然后读取数组,计算标准方差。不用数组的话,也可以将InfoTable重新读取一次。
4. 输出InfoTable类型的数据集。
以下是参考代码:
------------------------------------------------
// Get USL&LSL
var USL = spcTable.rows[0].USL;
var LSL = spcTable.rows[0].LSL;
//logger.info("USL="+USL);
//logger.info("LSL="+LSL);

// Get Sum
var sum = 0.0;
var spc1 = []; // array of spc1
var tableLength = spcTable.rows.length;
//logger.info("tableLength="+tableLength);
for (var x=0; x < tableLength; x++) {
    var row = spcTable.rows[x];
    spc1[x] = row.SPC1;
    //logger.info("spc1="+row.SPC1);
    sum = sum + row.SPC1;
}
//logger.info("x="+x);
//logger.info("sum="+sum);
//var result = sum;

// Get Average
var u = sum / tableLength;
//logger.info("u="+u);

// Get SD
var sd = 0;
for (x=0; x < tableLength; x++) {
    sd = sd + (spc1[x]-u) * (spc1[x]-u);
}
sd = sd/(tableLength-1);
sd = Math.sqrt(sd);
//logger.info("sd="+sd);

// Get CPU
var cpu = (USL-u)/(3*sd);
//logger.info("cpu="+cpu);

// Get CPL
var cpl = (u-LSL)/(3*sd);
//logger.info("cpl="+cpl);

// Get CPK
var cpk = 0;
if(cpu < cpl) {
cpk = cpu;
} else {
cpk = cpl;
}
//logger.info("cpk="+cpk);

// Output data
var params = {
    infoTableName : "InfoTable",
    dataShapeName : "DS_SPC_CPK_OUT"
};

// CreateInfoTableFromDataShape(infoTableName:STRING("InfoTable"), dataShapeName:STRING):INFOTABLE(DS_SPC_CPK_OUT)
var spcOut = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape(params);
spcOut.AddRow({Sum:sum, Avg:u, SD:sd, USL:USL, LSL:LSL, CPU:cpu, CPL:cpl, CPK:cpk, Qty:tableLength});

var result = spcOut;
------------------------------------------------

2019年11月4日

关于SQL SERVER的两个备忘



最近在用SQL SERVER做数据的后台自动处理,然后利用Thingworx展现。
这其中碰到两个问题,觉得较有代表性,在此做个备忘。

1. 关于游标内调用其它游标
我之前做过很多项目采用ORACLE数据库,其动态游标、物化视图、数组的功能非常NB,给我留下了深刻的印象。
但是我没想到在使用SQL SERVER游标时却踩到了坑。
SQL SERVER有一个全局变量@@FETCH_STATUS用来表示游标的状态,0表示尚未结束,-1表示已结束。
但是关键@@FETCH_STATUS是全局变量,这就意味着当第一个游标尚未结束时,当第二个游标的状态=-1时,也会将第一个游标强行结束。
解决的办法是用一个本地的变量,把@@FETCH_STATUS的值赋给此变量后再进行检查。
但是此办法逻辑上并不完备,如果有大量的并发,则可能会造成冲突。
因此如果需要配置后台任务,应该尽可能在时间上分散开来,以避免并发。
下面是相关的参考代码:

---------------------------------------------------------------------------
-- Normally, we use system variable @@FETCH_STATUS to check Cursor loop
DECLARE myCursor CURSOR FOR
SELECT fields FROM Table;
OPEN myCursor;
FETCH NEXT FROM myCursor INTO @myVar;
WHILE @@FETCH_STATUS = 0
BEGIN
  --DO somthing;
END
CLOSE myCursor;
DEALLOCATE myCursor;
 
-- But @@FETCH_STATUS is global variable
-- So if we use another Cursor within the loop, when inner loop is completed, @@FETCH_STATUS = -1, so the outer loop will also be closed.
-- Fixing method is to use local variable instead of @@FETCH_STATUS
DECLARE myCursor CURSOR FOR
SELECT fields FROM Table;
OPEN myCursor;
FETCH NEXT FROM myCursor INTO @myVar;
SET @Outer_loop = @@FETCH_STATUS;
WHILE @Outer_loop = 0
BEGIN
  --inner loop here
  DECLARE myCursor2 CURSOR FOR
  SELECT fields FROM Table2;
  OPEN myCursor2;
  FETCH NEXT FROM myCursor2 INTO @myVar2;
  SET @Inner_loop = @@FETCH_STATUS;
  WHILE @Inner_loop = 0
  BEGIN
    --Inner loop function
FETCH NEXT FROM myCursor2 INTO @myVar2;
    SET @Inner_loop = @@FETCH_STATUS;
  END
  CLOSE myCursor2;
  DEALLOCATE myCursor2;
  FETCH NEXT FROM myCursor INTO @myVar;
  SET @Outer_loop = @@FETCH_STATUS; 
END
CLOSE myCursor;
DEALLOCATE myCursor;
---------------------------------------------------------------------------

2. 关于关联字符集
由于数据库权限方面的限制,我在一个项目中需要对两个数据库中的表进行关联查询,然后碰到了字符集不匹配的报错。
具体的情况是这样的: 数据库DB1采用了GB字符集,数据库DB2采用了LATIN字符集,两个库的表T1和T2通过step_id进行JOIN,而step_id的数据类型是NVARCHAR,因此JOIN失败。
这是因为对于字符串类对象,虽然表示同样的值,当采用不同的字符集时,其存储时的值是不同的。
这个CASE给我的启示是,任何可能用于JOIN或外部引用的对象,要尽可能采用整型数据类型。
事实上,对于数据库来说,整型和日期型数据的检索速度是最快的,当有JOIN、分组查询、外部引用时,要尽可能使用整型数据。