2021年5月27日

ANDON背后的管理思想

在很多现代化生产制造企业内,ANDON系统是一个非常重要的管理子系统,具体业务分析可参考本人的文章《MES在汽车制造中的应用之业务篇(13) -- 安灯呼叫》。

本文从企业管理的角度出发,论述ANDON背后所体现的企业管理思想。

评价工厂综合能力的一个重要指标是生产效率,即工厂在单位时间内产出的产品数量,如汽车主机厂最重要的指标JPH是每小时产出的车辆数量。

那么要提高生产效率,主要有两种方式。

方式一,提高生产能力。方法有:通过工艺改进,改进设备的利用率;通过培训提高工人的技能;通过生产线节拍平衡,提高工厂的整体效率。

方式二,减少生产浪费。方法有:通过控制质量,减少产出不良品;通过班组长和工艺工程师支持,减少工人因技能不足造成的时间浪费;通过设备工程师支持,减少设备异常造成的时间浪费;通过物流部门支持,减少物料不足造成的时间浪费。

ANDON又叫求援系统,主要是通过获取援助来减少生产现场的时间浪费。

我们可以把工人和设备看成一线生产作业单元,因为工人和设备的作业时间、作业效率决定了工厂的整体作业时间、作业效率;而质量、工艺、设备、物流等部门是二线生产作业单元,这些部门通过对工人和设备的援助来间接地提升工厂的整体效率。

我们可以用军事术语做一个类比。

如果把一个生产任务当成一个作战任务,那么计划部好比参谋部,提前做好作战计划;物流部好比后勤部,兵马未动,粮草先行;生产经理好比前线指挥,负责执行作战计划;工人和设备好比兵和马,奋战厮杀推进阵地;班组长好比基层军官,协调指挥基层作战单元;设备部好比装备部门,兵有甲,兵有鞍,射手有箭,如此才有士气;质量部门好比督战队,确保作战原则。

有了这个类比之后,我们可以把ANDON求援这样翻译。

向班组长求援:班长,有人受伤,请求援助!

向工艺工程师求援:我部有损,不敌敌军,请求中军援助!

向质量工程师求援:兵心混乱,请求督战队弹压!

向装备部门求援:盔甲有损,请速修复!

向物料部门求援:粮草不足,速速支援!


Thingworx查看PDF和图片的方法

Thingworx提供了一个File Upload widget用于上传文件,文件存储在ThingworxStorage\repository目录。

我们可以利用Web Frame以实现对常用文件格式(PDF/JPG/BMP/PNG)的在线查看。

首先我们要理解Web Frame的原理,它相当于一个浏览器内置iFrame。

如果在一个Mashup内部使用Web Frame,那么Mashup对应的网页相当于主网页,而Web Frame调用的网页相当于一个内置网页,这两个网页有各自独立的进程。


下面介绍详细的配置和调用方法。

1. 设置Tomcat

编辑Tomcat 8.5\webapps\Thingworx\WEB-INF\web.xml,启用ClickjackFilterSameOrigin设置。

这部分的设置允许同一个域之间的网页进行相互调用。


2. 配置Thingworx PlatformSubsystem

Allow Request Method Switch = True

Filter Content Type = False


3. Web Frame URL调用格式:

https://<host:port>/Thingworx/FileRepositoryDownloader?download-repository=<YourThingWorxRepository>&directRender=true&download-path=/your_pdf.pdf

此处须注意:

1) Mashup和Web Frame的协议http/https必须保持一致,否则会被浏览器拦截。

2) download-path是文件所在文件夹的详细路径,如果有子目录,则须包含各级子目录。

3) 可以自己编写JavaScript Service,根据各项参数生成最终URL,然后绑定给Web Frame URL。

谈谈MES和ERP的差异

经常有网友问我MES和ERP的异同点,在这里我就谈谈个人的一些体会吧。

现代企业往往是哑铃型企业,即研发和销售两头大,制造过程中间小。

研发定义了产品的特点,销售将产品交付给最终用户,而制造过程将产品原型转变成实物。

从系统的角度来看,研发主要用PLM系统,销售主要用ERP系统,制造主要用ERP(计划、物流、采购模块)、MES、LES(物流执行)系统。

PLM和ERP针对的对象是一种/一类产品,如单价指的是此类产品在某段时间内的平均价格。

而MES不仅定义了某类产品的工艺过程,而且还要追溯到单件产品的生产过程,这是为什么呢?

前面说过,制造是将产品从原型转变成实物的过程,这个过程受到了人、机、料、法、环的限制。

人的方面,每个工人的知识、能力、熟练程度、身体素质都会存在差异,因此同样的生产过程,在不同的人操作时都会带来差异,从而影响产品的最终质量以及车间的生产率。

机器设备方面,也有品牌、控制系统、接口、使用时间、保养效果方面的差异,甚至同一批购入的机器也会有差异。

材料方面,有供应商、批次的差异,因此很多MES都有模块进行材料批次的追溯。

规范方面,MES可能包含一些动态的过程,如A工位检查间隙,B工位选择垫片,B就依赖于A的实际结果。

环境方面,有一些作业对环境因素(如温度、湿度)非常敏感,工艺过程需要将环境因素作为输入参数。

因此MES不仅要面对这些差异,而且要将这些差异进行量化管理。

另一方面,从企业的角度来说,PLM和ERP都非常强调产品的稳定,而MES则存在更多的变化和调整。

这是因为PLM和ERP管理的都是一种/一类产品。在PLM中如果产品有较大的变化,通常我们会创建新产品进行区分。而在ERP中如果产品有较大的变化,其在计划、采购等方面都会带来巨大的管理成本,因此ERP非常强调产品的稳定。

针对制造过程的考核指标主要是:质量、生产率、成本,这三者都要求生产部门进行持续地优化,而优化就意味着差异化、改变。

如质量优化的方法,就有改进工艺过程、提高检验标准。

生产率优化的方法,就有人、机、料、法、环的改进。

成本的降低,就意味着减少不必要的浪费和损耗,通常也包含工艺过程的改进。

因此在制造过程和MES中,差异化是不可避免的:不仅没有所有行业通用的解决方法,同一行业也会有相当多的差异,甚至同一企业内部、同一工厂内部,针对相同产品也要进行差异化管理。

这就是MES相较于ERP更为百花齐放的原因,也是大的MES系统通常自带集成开发工具,以满足用户差异化开发的原因。



SQL Server优化笔记 – 索引部分

1. 聚集索引

通俗地来说,聚集索引是表在新增数据时的排序方式。如字典以字母排序,小说以章节排序,历史书以区域排序等。

很多人以自增的ID主键作为表的聚集索引,这种定义能够满足唯一性要求,也不会增加索引碎片。

但是除了对表进行周期性处理外,在业务上我们很少把ID作为WHERE的条件,因为自增ID很少有业务含义。

相反,我们应该思考,在查询时,我们会利用哪个WHERE条件,把要查询的数据从一个非常大的数据集筛选出来。

例如,我们有一个OrderInfo表,存储了10年的工单数据,表里有ID(自增变量)、OrderNumber(工单号)、ScheduleDate(计划时间)、CreatedOn(创建时间)等字段。

在日常业务上我们通常只查询近3个月创建的工单,那么我们可以联合CreatedOn和ID这2个字段创建聚集索引(ID字段保证唯一性),在查询时我们总是带上WHERE CreatedOn > GETDATE() – 90这个条件,那么我们可以很快通过聚集索引定位到一个非常小的数据集。


2. 非索引列的定位

同样以OrderInfo表为例,假如我们为ScheduleDate创建非聚集索引。

如运行以下SQL,以获取计划时间为最近一周的数据:

SELECT

OrderNumber,

ScheduleDate

FROM OrderInfo

WHERE CreatedOn > GETDATE() – 90

AND (ScheduleDate BETWEEN GETDATE() AND GETDATE() + 7)

ORDER BY ScheduleDate;

我们可以看到用于筛选的2个字段CreatedOn和ScheduleDate都已经建立了索引,因此筛选速度很快。

但是在输出的2个字段中,ScheduleDate已有索引,而OrderNumber却没有索引,那么数据库是怎么定位的呢。

数据库在创建索引的时候,除了索引引用的字段外,还会包含一个隐藏的字段,即表的原始行号ROWID。

在此例中,ScheduleDate通过索引查找定位(Index Seek),同时得到ROWID,再通过ROWID得到OrderNumber的值(Cluster Index Scan)。

如果表的数据量大,那么Cluster Index Scan要扫描的记录数非常多,也要尽可能避免,改进的方式是针对要SELECT输出的字段都加上索引。

也就是说,不仅是WHERE字段、GROUP BY字段、ORDER字段要建立索引,SELECT字段也要建立索引,而且常用的查询要尽可能建立多个字段复合索引。


3. 类型转换

类型转换会使索引失效。这是很多人会犯的错误。

例如:

1) 日期和字符串转换

错误:WHERE CONVERT(VARCHAR, OrderDate, 111) >= '2011/06/01'

正确:WHERE OrderDate >= CONVERT(DATE, '2011/06/01', 111)


2) 大小写转换

错误:WHERE UPPER(FirstName) = 'ALICE'

正确:新建一字段并建立索引,在新增数据时,将UPPER(FirstName)值写入此字段,然后将此字段用于WHERE条件。


3) 字符串LIKE操作

错误:WHERE SalesOrderNumber LIKE 'SO43%'

正确的做法是从要比较的字段中拆出一个字段,在此字段中建立索引,然后在此字段中建立WHERE比较,符号为=或<>。


4) 数值运算

错误:WHERE UnitPrice * Qty > 10000

正确:创建计算字段ALTER TABLE ADD COLUMN TotalPrice AS UnitPrice * Qty,在此字段上建立索引。


4. 视图索引

视图上也可以建立索引,但是创建视图时必须包含WITH SCHEMABINDING。


机加工刀具的管理流程和系统设计

在机加工制造过程中,刀具是影响产品最终质量的重要因素,本文就谈谈刀具相关的管理流程和系统设计。

一个典型的制造企业是由研发和销售驱动的,研发定义了一款产品,来自销售部门的订单转化成生产工单,触发了将产品变成最终实体的制造过程。

工单包含了产品的物料BOM信息,即需要投入哪些物料。

在制造过程中,我们需要将物料BOM细化到工序、工位。如某产品物料BOM包含4颗螺栓A,在工位1由工人放置,在工位2预拧紧,在工位3终拧紧。我们可以看到多出了额外的信息,从BOM的角度来看,工位1:数量4,工位2:数量0,工位3:数量0。此类BOM属于工艺BOM的范围,在这里我们称之为1类工艺BOM,它的特点是各物料的总数和MBOM是一致的。

而在制造的过程中,还需要配合一些MBOM之外的设备及材料,如刀具、夹具、量具、试验台等。这些设备的物料或准备参数也是与产品相关的,但是不作为最终产品的一部分销售,因此不在MBOM中体现,在这里我们把相关的BOM称为2类工艺BOM。

产品的刀具BOM就是一个典型的2类工艺BOM。当机床需要更换加工产品时,往往我们需要更换及准备相应的刀具。

在管理刀具时,我们可以在2个层面进行管理:刀具组件和刀具零件。

刀具以组件的形式进行加工作业,但在采购和存储时也可以以零件的形式进行管理,因为共用件非常多,以零件形式运营可以减少库存和运营费用。

本文先以组件的形式进行介绍,之后再补充零件相关信息。

 



一个刀具的生命周期包括:买入、测量、使用、维修、报废等状态。

测量:由于刀具在使用后,刀头和加工件接触后会消耗一小部分,在物理形态上会有细微的变化,因而我们需要使用专用设备进行测量,具体数据会作为补偿值录入机床,以便在下次加工时得到所需的精度。

维修:刀头在使用一定的时间后,其锐度会降低,因此需要修刀,以恢复其锐度,以满足加工要求。

报废:刀具或刀头有一定的使用寿命,达到一定的加工时间后,刀具的强度降低,不能满足加工要求,否则会造成断刀,甚至进而造成加工件报废。

那么针对这些流程,假如我们需要设计一个刀具管理运营系统,需要考虑哪些因素呢?

首先看测量。测量设备(对刀仪)是精密仪器,对稳定性要求高,一般和机床不在同一个物理位置。测量设备输出的数据,需要录入机床HMI。因此有一个数据传输的需求。这个过程如果手工做的话,效率很低,也容易出错。

一些测量设备如Zoller支持通过网络传输数据,但是由于机床支持的传输协议、数据格式有一定的限制,且不同厂家的机床之间没有统一的标准,因此此项方法有一定的局域性,但是如果一个机加车间有大量的同型号机床、对刀仪,则可以联合供应商一起开发,这样做的效率是最高的。

还有一种方式是通过条码(一维码或二维码)来传输数据。我们可以在对刀仪输出测量报告时生成相关的条码,然后在机床HMI上扫描条码以录入数据,这样也可以减小数据输入错误的概率。

另有一种方式是通过RFID标签,详见下文。

维修可通过经验或测量进行判定。

报废或者说刀具使用寿命的判定非常重要,也有不同的管理方法。

一般来说,供应商在刀具出厂时,会说明其使用寿命。在加工时,我们需要对刀具的实际使用时间进行累加,当累加值大于使用寿命时,则需将刀具报废。

在设计系统时,我们尤其要考虑:实际使用时间的数据存储方式、累加的方法。

如果刀具固定安装在某机床上就比较简单了,我们可以定义一个规则,为每把刀具建立编号,并在机床系统里定义对应的系统变量。我们可以在程序中编写指令,当对应的加工程序完成时,自动将刀具变量累加。

如果刀具没有固定安装在某机床上,将必须能够让机床识别刀具的编号,此外需有一种可靠的方式存储其实际使用时间并且能够被机床识别。考虑到机床的工作环境,使用RFID标签是一种不错的选择。如巴鲁夫Balluff提供螺钉形式的RFID标签,安装在刀柄的一侧,我们可以在对刀仪电脑、机床电脑上安装RFID读写器,从而实现机床与对刀仪之间的数据传输,并且持续记录刀具的设定使用寿命和实际使用时间。

 


接下来我们再看一个更加精益的流程:综合刀具组件和刀具零件的方式进行刀具管理,即以刀具组件的形式进行测量和加工,但是以刀具零件的形式进行采购、存储、修理、报废。

从下图我们可以看到,一个刀具组件由以下零件组成:刀柄(Holder/Arm)、刀面(Face)、刀头(Insert/Cutter)。刀头起到实际上的加工作用,需要固定安装到刀面上,然后结合刀柄安装到机床上。这其中,刀面和刀柄都是非消耗件,只有刀头是消耗件。而且相同的零件可以被用到不同的刀具组件上。采用零件的形式进行管理,可以增加刀具的利用率,减少刀具的采购成本。

当刀具库房接到一个新工单时,需要根据工单展开得到刀具组件清单,并且进一步得到刀具零件清单,这里需要做2次BOM展开。此次,还需要将刀具零件组装成组件,然后将库房位置转移到机床。这里的操作与物流配送系统的Kitting及移库操作非常相似。

 




2021年1月28日

也谈“低代码开发”

 


有段时间,“低代码开发”成为一个热词,有很多大佬鼓吹,让不明真相的群众听得一愣一愣的,以为软件开发变得非常容易了。

Scratch和少儿编程班的流行,给很多人带来了“编程=拼积木”的印象。

而我的体会是,低代码开发仅仅降低了这个行业的入门门槛,但是对于大型软件工程来说几乎没有什么用处,甚至会降低效率。

比如我曾用过两个MES开发平台:Apriso Flexnet和PTC Thingworx。

我对Apriso Flexnet的最深印象是,用一整张A0纸打印出来的某个核心功能的引用逻辑。

Flexnet允许用图形化的方式来定义引用关系、触发条件,但是面对一个复杂的功能时,这些引用逻辑纵横交错,看图定位会让人抓狂。

Thingworx更为低代码,我对它的定位是轻量级的工业物联网应用开发平台,它能够以图形化的方式对数据、事件、触发条件进行映射,因此在开发轻量级的应用时非常方便。

但是在开发大型项目时会让人抓狂,如下图是某个功能的前端、后端引用关系: 



下图是某个核心函数的引用关系: 



从右上角的进度条可以看出,此函数绑定的数据、函数实在太多了,你甚至很难保证绑定到了正确的位置。在这种情况下,图形化的方式反而降低了效率。



2020年11月24日

Rebuilding SQL Server Indexes


Highlights:

- Check index fragmentation: average percentage, page count

- ALTER INDEX REBUILD

- Manually set Timeout parameter

- Set counter to control session frozen time

- Sleep to release suspended sessions

- EXEC sp_who2 to check real time deadlocks


1. Background

A lot of systems have part of logic running in Database, and as time passed, data and indexes increased, and then Database performance will decrease.

One of key factor of Database performance is Index.

When data and indexes increase, the fragmentation level increase as well.

According to Microsoft article “Reorganizing and Rebuilding Indexes(https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2005/ms189858(v=sql.90))”, we should Reorganize indexes when average fragmentation is between 5% and 30%, and we should Rebuild indexes when average fragmentation is greater than 30%.

We can use below SQL to check average fragmentation of indexes which are greater than 30%:

-------------------------------------------------------------------------------------

SELECT dbschemas.[name] AS 'Schema'

,dbtables.[name] AS 'Table'

,dbindexes.[name] AS 'Index'

,indexstats.avg_fragmentation_in_percent

,indexstats.page_count

FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, NULL) AS indexstats

INNER JOIN sys.tables dbtables ON dbtables.[object_id] = indexstats.[object_id]

INNER JOIN sys.schemas dbschemas ON dbtables.[schema_id] = dbschemas.[schema_id]

INNER JOIN sys.indexes AS dbindexes ON dbindexes.[object_id] = indexstats.[object_id]

AND indexstats.index_id = dbindexes.index_id

WHERE indexstats.database_id = DB_ID()

AND indexstats.avg_fragmentation_in_percent > 30

--AND dbschemas.[name] = 'dbo'

--AND dbtables.[name] = 'RPV_MAGPLANBEL'

--ORDER BY indexstats.page_count DESC;

ORDER BY indexstats.avg_fragmentation_in_percent DESC;

-------------------------------------------------------------------------------------


We also need to pay attention to page count, each page stores about 8KB data, so a big page count means the rebuild time will be quite long.


2. Considerations

1) Use ALTER INDEX index_name or ALTER INDEX ALL

Use ALTER INDEX index_name ON Table REBUILD can only rebuild 1 index with a command.

ALTER INDEX ALL ON Table REBUILD can rebuild all Indexes of a Table with a command, but it require longer shut down time, because the new SQL might be suspended during the rebuild process.

2) Manually set remote timeout to avoid timeout disconnect:

EXEC sp_configure 'remote query timeout', 600 ;

RECONFIGURE ;

Since rebuilding process might take quite long time, which could be longer than default remote timeout time, so it will be better to manually set it.

3) Set counter to control session frozen time

Even if we’re using PRINT() in SQL Server Management Studio(SSMS), the print out messages will only be showed after the whole SQL block has completed execution.

During the SQL execution, the Messages Tab is blank, so during that time we have no idea what’s going on or going wrong.

So it will be better to spilt the whole session into many small sessions, and we can use Counter to control it.

Now we change the SQL of checking index fragmentation, to order by page_count desc:

-------------------------------------------------------------------------------------

SELECT --dbschemas.[name] AS 'Schema',

dbtables.[name] AS 'Table',

dbindexes.[name] AS 'Index',

indexstats.avg_fragmentation_in_percent,

indexstats.page_count

FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, NULL) AS indexstats

INNER JOIN sys.tables dbtables ON dbtables.[object_id] = indexstats.[object_id]

INNER JOIN sys.schemas dbschemas ON dbtables.[schema_id] = dbschemas.[schema_id]

INNER JOIN sys.indexes AS dbindexes ON dbindexes.[object_id] = indexstats.[object_id]

AND indexstats.index_id = dbindexes.index_id

WHERE indexstats.database_id = DB_ID()

AND indexstats.avg_fragmentation_in_percent > 30

AND dbschemas.[name] = 'dbo'

--AND dbtables.[name] = 'RPV_MAGPLANBEL'

AND dbindexes.[name] IS NOT NULL

ORDER BY indexstats.page_count DESC;

--ORDER BY indexstats.avg_fragmentation_in_percent DESC;

-------------------------------------------------------------------------------------


So for these indexes with such huge page count, we should rebuild 1 index each time.

And when page count is getting smaller, we can set counter into a bigger number.


4) Set sleep to release suspended sessions.

If we’re running multiple REBUILD operations in same session, we should sleep at least for 5 seconds in between, so the suspended SQLs will be run and released.

By using this:

WAITFOR DELAY '00:00:05';

5) Check real time deadlocks before and during REBUILD.

By running:

EXEC sp_who2

We can see deadlocked sessions(Status=SUSPENDED, or BlkBy IS NOT NULL), the BlkBy points to the session who caused the deadlock.

We should run REBUILD when the moment no deadlocks are found.

Another SQL to list deadlocks:

SELECT * 

FROM sys.dm_exec_requests

WHERE DB_NAME(database_id) = 'KBS_FDM' 

AND blocking_session_id <> 0;


3. Optimized SQL:

-------------------------------------------------------------------------------------

Declare @getTables CURSOR; -- Cursor to list out all indexes have frag % > 30

Declare @TableName varchar(255);

Declare @IndexName varchar(255);

Declare @command nvarchar(4000); -- Detail ALTER INDEX command

DECLARE @DT NVARCHAR(50); -- Datetime to print out

DECLARE @i INT; -- Counter


-- set remote time out

EXEC sp_configure 'remote query timeout', 600 ;

RECONFIGURE ;


SELECT @DT = CONVERT(NVARCHAR, GETDATE(), 121); --YYYY-MM-DD HH:MI:SS

PRINT(@DT);

PRINT('Rebuilding Index started.');

PRINT('');

--SET @getTables = CURSOR for SELECT name FROM sys.objects WHERE type = (N'U');


--EXEC sp_who2 -- to check existing deadlock(BlkBy)


SET @i = 0;

SET @getTables = CURSOR for

SELECT --dbschemas.[name] AS 'Schema',

dbtables.[name] AS 'TableName',

dbindexes.[name] AS 'IndexName'

--indexstats.avg_fragmentation_in_percent,

--indexstats.page_count

FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, NULL) AS indexstats

INNER JOIN sys.tables dbtables ON dbtables.[object_id] = indexstats.[object_id]

INNER JOIN sys.schemas dbschemas ON dbtables.[schema_id] = dbschemas.[schema_id]

INNER JOIN sys.indexes AS dbindexes ON dbindexes.[object_id] = indexstats.[object_id]

AND indexstats.index_id = dbindexes.index_id

WHERE indexstats.database_id = DB_ID()

AND indexstats.avg_fragmentation_in_percent > 30

AND dbschemas.[name] = 'dbo'

--AND dbtables.[name] = 'RPV_MAGPLANBEL'

AND dbindexes.[name] IS NOT NULL

ORDER BY indexstats.page_count DESC;

--ORDER BY indexstats.avg_fragmentation_in_percent DESC;


OPEN @getTables;

FETCH NEXT FROM @getTables into @TableName, @IndexName;

--WHILE @@FETCH_STATUS = 0

WHILE @i < 1 AND @@FETCH_STATUS = 0 -- Set @i to bigger number when page count is getting smaller

BEGIN;

SELECT @DT = CONVERT(NVARCHAR, GETDATE(), 121); --YYYY-MM-DD HH:MI:SS

PRINT(@DT);

--set @command = N'ALTER INDEX ALL ON ' + @TableName + N' REBUILD;'; -- WITH (ONLINE=ON);'; for enterprise version only

set @command = N'ALTER INDEX ' + @IndexName + N' ON ' + @TableName + N' REBUILD;';

PRINT(@command);

--PRINT('');

--WAITFOR DELAY '00:00:01';

BEGIN TRY  

EXEC (@command);

SELECT @DT = CONVERT(NVARCHAR, GETDATE(), 121); --YYYY-MM-DD HH:MI:SS

PRINT(@DT);

PRINT('Rebuilding Index completed.');

PRINT('');

END TRY  

BEGIN CATCH  

SELECT @DT = CONVERT(NVARCHAR, GETDATE(), 121); --YYYY-MM-DD HH:MI:SS

PRINT(@DT);

PRINT 'CATCHED!';

END CATCH;  

WAITFOR DELAY '00:00:05'; -- wait 5s to release suspended sessions

FETCH NEXT FROM @getTables into @TableName, @IndexName;

SET @i = @i + 1;

END;

CLOSE @getTables;

DEALLOCATE @getTables; 

SELECT @DT = CONVERT(NVARCHAR, GETDATE(), 121); --YYYY-MM-DD HH:MI:SS

PRINT(@DT);

PRINT('All indexes rebuilt.');

-------------------------------------------------------------------------------------


4. PS: WITH (ONLINE=ON) condition.

While running ALTER INDEX (ALL) REBUILD, there’s an optional parameter called ONLINE.

If we set ONLINE=ON, that makes targeted index still available during most time of rebuild process, but that’s only available for SQL Server Enterprise version.


2020年9月15日

Thingworx心得之Events Router与Expression的限制

1.     Events Router的限制

Thingworx尽管参考了面向对象的设计,但是作为一个工业物联网平台,它简化了很多功能。

比如,我们不能直接利用某个ServiceExpression直接给Property赋值,而只能通过Data Binging的方式进行绑定,并且一个Property只能绑定一个数据源。

但是有时候我们有多个数据源,这时就需要自己写Service或者使用Events Router组件。

如下图所示,我们希望点击“Output A”,则输出A;点击“Output B”,则输出列表选中的数据。

其中,列表选中项先绑定到Mashup参数Input2,以模拟Contained Mashup传递的参数。



实际测试:

1)     点击Output A,输出A,正确。

2)     列表选中Area,点击Output B,输出Area,正确。

3)     点击Output A,输出A,正确。

4)     列表选中Area,点击Output B,输出A,错误!

这是因为,Input 1对应的数据来自于Expression输出,每次执行以后,系统都会认为其值已变化(Data Change Type=Always,尽管数值可能不变)。而Input2对应的数据来自于Mashup参数,只有其数值变化时系统才会认为其值已变化。

 

解决办法是强制让Input2发生变化,如增加一个Events Router,并将Events Router 1Output作为Input1,这样的效果相当于Output A执行时,将Input 2强制复位:



2.     Expression的限制

Expression的作用和Service类似,主要用于客户端的简单处理,其限制为:

1)     不能定义临时变量。

2)     不能输出InfoTable

3)     只有Changed事件,没有ServiceInvokeCompleted事件,因此无法严格区分多个Expression的执行顺序。

 

 

2020年8月21日

美剧式的笑傲江湖 - 游戏《巫师3》赏析

去年买了台二手XBOX ONE,玩了很多畅销大作,如《使命召唤》、《战地》、《绝地求生》、《刺客信条》、《FIFA》等,也利用XGP帐号玩了很多免费游戏如《荒野大飙客2》,但是我最爱的还是《巫师3》。

我在第一遍快速通关《巫师3》后,也找了一些广受好评的RPG游戏玩,如《上古卷轴5》、《刺客信条之奥德赛》、《荒野大飙客2》,但是总感觉没有《巫师3》的那个味。

作为一个中年人,我玩游戏最主要的是看故事,进入一个陌生世界,看看那里的人间百态。

相比较而言,《上古卷轴5》有太多的抉择,而我只想做观众,不想做主角;《荒野大飙客2》和《GTA5》都是黑帮小人物的故事,而这样的背景对我无感;《刺客信条之奥德赛》的故事太乱颇为无趣。

于是我又打开《巫师3》,开始二周目,这次我把精力放在所有的支线任务、狩猎和寻宝上,我耐心地看所有过场动画、对话、文字,任由自己沉浸其中。

如果用一句话总结我对《巫师3》的感受,那就是:美剧式的笑傲江湖。

游戏《巫师》系列继承了奇幻小说《巫师》系列的世界观和人物设定,总体框架宏大,可类比《冰与火之歌》。

在场景设计方面,有低地的沼泽,有中原的都市,有海边的群岛,有高山的宫殿。

在故事发展方面,也是有多条故事线同时发展。如血腥男爵的故事,一条线是他的家庭矛盾,另一条线是沼泽女巫对当地人的控制。在诺维格瑞,更是有3条线同时发展:四大黑帮的斗争,两大王国的战争,永恒之火与女术士集会所两大宗教团体的斗争。但是对于一个活了一百多年的巫师(猎魔人)来说,这些都算不上什么事,因为他只是一个从容的过客、一个无心的看客。

为了推动剧情,游戏制作了大量的过场CG动画,其中大量动画采用动作捕捉制作,因此很多动作和表情都非常令人信服。

巫师(猎魔人)经过身体改造,已经不能算是完全的人类,因此他和人类社会既存在联系,又有一定的距离。这一点和武侠世界中的侠客很象。

主角杰洛特对权力和金钱都没有太多的欲念,主线有两条,一条是寻找女儿希里,另一条是和女术士椰奈法、特丽丝的感情发展,这两条线的进展决定了最终的结局。

但是《巫师3》最令人赞叹的并不是宏大的整体叙事,而是无数小人物的支线故事,而主角在面对他们时往往会面临艰难的抉择:如血腥男爵家暴妻子,但不知道妻子出轨,要不要告诉他真相;如猎人会变成狼人,这一点他自己和妻子都不知道,而爱他的小姨子知道,然后她诱骗猎人变身杀死自己的姐姐;如村民雇佣巫师除魔,成功后却想杀了他来逃避酬金。有太多令人感慨的故事,有太多令人无从下手的抉择。

相对于平民,巫师能够做出一定程度的审判,但任何时候他都能转身离开。

主角在由人类转变成巫师的过程中,因为魔药的作用,从此没有了人类的情感,因此他已是郎心似铁。因此在任何时候,他既可以行动参与其中,也能够视若不见。

《巫师3》的游戏类型为ACTION RPG,有其丰富的动作系统:钢剑砍人兽,银剑斩妖魔,另外还有法印、剑油、炸弹、魔药等,既有物理攻击,也有魔法攻击,手残的玩家也可以通过跳跃闪避苟活。虽然不是最出色的,但是可玩性还是颇高的,特别是注魔后的剑舞很好看。

游戏的整体节奏感非常好,音乐也配合得非常到位,特别在衬托氛围和调动情绪方面,是一个很大的加分点。

最后补充几个游戏的小技巧:

1)     角色技能里激活“美食家”,吃食物时能够自动回血。

2)     冥想能够满血,并补充煎药和炸弹。

3)     常备煎药:猫眼夜视、金莺防毒、拉法达回血、鲸心屏气。

4)     可以通过寻宝任务获得猫派或狮鹫派的装备蓝图,然后找工匠打造全套装备。

5)     激活《石之心》DLC后,执行支线任务“注魔”,完成后可以为剑注魔,比如“断裂”、“护身”。注入“断裂”后的剑舞又秀又能打。

6)     增加金币的最简单方法是消灭流氓和盗贼,然后捡他们的兵器换钱。

7)     与商人和工匠换钱时,如果对方金币不够,可用冥想跳时间,一般跳个五六天对方金币又增加了。

游戏版本建议购买年度版,包括本体的《石之心》、《血与酒》两个出色的DLC。整个游戏所有的支线任务能够玩数月至半年的时间,也就不到200块钱,非常划算。

 

 

2020年8月5日

从TikTok看数据主权


近十年来,互联网经济飞速发展,我认为在很大程序上得益于以下几个关键因素:
1.     企业在数据的获取上几乎免费,在数据的使用上几乎不受限制,在数据的保护上几乎没有责任。
2.     当信息作为一种观点时,企业作为技术中立方,免受舆论的指责。
3.     当信息作为一种商品时,企业利用免费策略向新兴领域进行倾销,却没有受到惩罚。
现在情况即将发生巨大的变化。
首先我们来看这3个因素的不合理之处:
1.     数据的拥有者是用户,但是用户很少实质上拥有数据的归属权。企业在获取数据时,没有向用户付费。很多企业偷偷地采集数据时,却没有告知用户。当用户数据丢失时,企业也没有给用户赔偿。
2.     很多平台自动从网上采集信息,然后通过算法自动向用户推送,事实上起到了媒体的作用。和传统媒体的区别是用算法代替了编辑,此外用户评论作为即时反馈会放大文章的舆论力量。
3.     即便我们承认全球化和自由贸易,但是一定程度的贸易保护是不可或缺的。而信息服务是一种特殊的商品,网络贸易保护的正面例子是百度战胜谷歌,负面例子是欧洲在社交应用领域全面溃败。
现在随着逆全球化的发展,很多前置条件将被推翻。
GITHUB的例子告诉我们,每一条信息都有国籍,每一个比特都有国籍。
从前是政府要求大公司删除数据,但是将来公司只是数据的托管方,政府对数据有管理权。
当信息作为一种观点时,政府会出于公共安全的考虑,对数据进行监管。在可见的将来,提供观点的社区或平台会被要求实名制,当A国的网民发出文章时,他的文章会被A国监管;当B国的网民对他的文章进行评论时,此文章和评论将同时被AB国监管。也就是说,数据的拥有域带有主权的属性。
当信息作为一种商品时,政府会出于贸易保护的考虑,对数据进行拦截和过滤。防火墙是一种方便的工具。此外,免费销售和发行有可能受到反倾销调查。
因此,信息作为观点和商品,政府会出于社会安全和贸易保护的考虑,对其进行监管。在政府的眼里,数据有价、有风险、有立场,因此必须被纳入主权管理。