FLEXSCHE版本19
2019年12月27日发布了FLEXSCHE版本19.0。实施了174项的功能扩展。在此介绍一下其中的主要功能。(版本18.0和版本19.0的差异)
FLEXSCHE WebViewer
可以免费在Web浏览器阅览简单的甘特图表。
对制造现场发行生产排程系统的计划结果有很多时候都是针对各个工人或设备的局部的“工作指示”,另外有些时候想使用甘特图等对全体进行俯瞰吧。还有领导层(总公司或厂长等)以及销售部门也想随时把握工厂的状况吧。
对于这种需求传统方式提供了FLEXSCHE Viewer。这个与计划员所使用的FLEXSCHE GP具有相同程度的高级画面展示功能、虽然可以从各种各样的角度对计划进行可视化,但由于每个客户端都需要许可证,其难点在于增加可查看客户端数量时成本随之升高。
所以此次发布版本19的同时,开始提供FLEXSCHE WebViewer(略称FWV)。FLEXSCHE WebViewer是在网络浏览器上使用的简易甘特图表查看器。虽然可显示的信息有限,但可以满足“简单也没有关系想要掌握全体状况”这种需求。
FLEXSCHE WebViewer可以免费使用,但只可以显示从参加了维护合同的FLEXSCHE GP或者FLEXSCHE CarryOut的许可证的产品所输出的数据。
输出WebViewer数据
在参加了维护合同的FLEXSCHE GP上设置对FLEXSCHE WebViewer的输出方法,进行数据输出处理。将在此制作的设置文件放到FLEXSCHE CarryOut服务器上后也可以从FLEXSCHE CarryOut输出数据。
甘特图表内显示的字符串、颜色、图表行的配置方式等、可以使用在FLEXSCHE常用的计算表达式定义。另外可以对图表行进行树状的层次化。
FLEXSCHE WebViewer服务器
将输出的数据放在一般的Web服务器上就可以阅览FLEXSCHE WebViewer,在内网运作FLEXSCHE WebViewer服务器即可在数据更新时自动向浏览器推送。
另外WebViewer服务器的动作环境无论什么种类的OS只需要Node.js即可。
请看FLEXSCHE WebViewer的示例画面。但由于未启用FLEXSCHE WebViewer服务器在此无法确认更新数据的推送功能。
图表的操作方法请点击画面右上的?查看。
对应浏览器是Google Chrome / Mozilla FireFox。Microsoft Edge在版本79之后(chromium版)已确认可以使用,但由于在此之前的版本未对应,无法显示工作连接线等、一部分的功能无法使用。
软件方式许可证
可以选择非硬件密钥(加密狗)许可证
从而更便于在数据中心的授权管理。
发布了不使用硬件密钥的软件方式的许可证系统"软件方式许可证"。 由于无需USB许可证密钥,没有USB端口的服务器或虚拟机上可以安装许可证。 另外与硬件密钥不同丢失的风险大幅度降低。
万一出现安装了许可证的电脑发生故障无法启动时维护服务能够为您重新发行许可证。
FLEXSCHE Performance Tuning Service
面向维护用户的新服务
FLEXSCHE Performance Tuning Service (FPTS)正式开启。
虽然FLEXSCHE力争以最短的排程时间挑战极限进行高速化处理、 但在大规模数据或复杂的排程逻辑的情况下有时候还是需要比较长的处理时间的。 有时候会有想尽可能缩短时间这样的要求,所以开始了回应这种需求的新服务。
本服务需要客户自身操作FLEXSCHE收集Profile信息, 我公司基于该信息为客户制作为该客户量身打造的专用FLEXSCHE安装程序。 由于聚焦客户固有数据对FLEXSCHE的各个程序模块进行最优化处理, 在该数据、逻辑以及操作方法的情况下可以期待FLEXSCHE处理速度的提升。
模块提供的流程
- Profile信息收集
请准备运用数据或和其同等的数据。 和运用时进行相同操作,制作该处理的特性和倾向信息(Profile)。 由于包含Profile收集模块,需要事前对用模块替换通常模块。
※运行Profile收集模块比较耗时。有可能需要传统方式的10倍。
- 提出Profile
向FLEXSCHE Corporation发送收集的Profile。
- 构建专用模块(FLEXSCHE Corporation)
基于送来的Profile我公司为客户构建专用模块。
※需要数个工作日。
- 取得专用模块
完成构建时会以邮件方式通知。可以下载进行高速化处理的模块。
- 适用专用模块
用下载的专用模块替换FLEXSCHE的通常模块。
FPTSManager和FPTS服务器
进行上述手续使用FPTSManager工具。 Profile的制作和提出、构建好的专用模块的取得、模块替换的操作都用此工具。 另外我公司运作着FPTS服务器的web服务,由于FPTSManager与该服务器交互信息, 基本上无需和我公司人员直接取得联络。 同时也无需用资源管理器直接操作文件。
维护模式可以进行模块替换和FLEXSCHE的启动。返回适用高速化模块前的状态、可以便于检验或对比基于FPTS高速化的效果。 另外在此也可以进行旧文件的删除等操作。
服务相关的其他补充
- 仅对应维护对象版本的最新发布版本/修订版本。
- 无法承诺可提速的具体数值。
- 由于FLEXSCHE已经高速化处理到了极限,根据数据或处理内容可能无法看出效果。甚至可能出现反而转慢的情况。
- Profile信息只是程序的动作倾向相关的汇总信息。不包含项目固有的信息请放心。
- 导入FLEXSCHE的PC在离线环境的时候,向在线的PC复制FPTSManager可以实施与FPTS服务器的手续部分。
- 在修改版发布后需要对该发布的高速化模块时需要重新进行Profile收集手续。
本服务仅供参与维护合同的许可证。
调试追踪功能
观察排程中的状态的机制。
随着排程规则的构建,当排程结果与预计不同时、有时候会难以找到为何会不同的原因。 在这种状况下、途中暂停排程处理(=break)、想要仔细观察状况吧。
版本19的“调试追踪功能”可以实现。
在排程处理的各处设置了“断点”,启动后可以break。 断点可以通过计算表达式设置“断点条件”,这样就可以满足特定条件的时候break。 为了显示在break时内部状态可以对各个断点自由设置计算表达式。
调试面板
聚合了调试追踪功能的“调试面板”可以进行各种跟踪操作。通过1步步进行排程处理的“步骤执行”、推进到满足断点条件处的“继续”、 在甘特图表上显示最后处理的对象(工作等)“搜索目标对象”等,可以迅速找到问题的出处吧。
break时除了一部分命令外可以通常进行切换画面、滚动、扩大缩小、对话框等操作。 就连鼠标拖动工作都可以进行,也许根据规则的内容可能出现以外状态、但在构筑排程规则时一定会事半功倍。
另外仅在将用户区分设置为“高级用户”以上时可用。设置为“一般用户”无法使用该功能。
高DPI对应
高DPI环境下FLEXSCHE也可以顺畅运作。
笔记本PC或平板PC使用高分辨率屏幕但小画面环境(高DPI环境)时FLEXSCHE也可以顺畅运作了。高DPI环境下一般都是扩大系统字符大小。 应用需要有相应处理,单纯扩大应用字符会出现锯齿状,另外UI的绘图也会很粗糙。
此次的对应根据系统字符大小FLEXSCHE也根据情况相应的放大UI部分、比以往的扩大模式比实现了更细腻的外观。
EDIF
对EDIF以及EDIF配置工具强化了如下功能。
导出班资源子任务
可以导出对班资源分派的任务的更为详细的子任务(对班成员的分派状态)。 同是对应了字段分离、表格分离,另外表格分离时可以使用计算表达式。
带键自定义集的事件对应
对应了与t_tran、t_last_updated字段的映射。 可以和一般的FLEXSCHE表格一样进行相同的事件处理。
实绩·库存操作·自由日历等差异对应
对记录保持代码可以对应差异导入/导出。
指定ADO、ADO.NET的命令超时
ADO/ADO.NET也对应了外部表格设置时对CSV文件设置的超时栏、 对DB命令也可以进行超时设置了。
导入时直接指定SQL
可以执行任意的SQL查询,自由刷选从DB取得的记录了。 从大量数据向FLEXSCHE导入一部分的时候可以期待比以往更为显著的提速。
Select * from tableA where xxx='val1'
指定这样的SQL文即可在FLEXSCHE接受数据前的阶段进行筛选。 以往时对于所有的记录FLEXSCHE进行排查决定取舍,从DB取得成本较高。 数据量越多收益也越大。
由于其高自由度请注意update或delete文。
易于找到出错位置
包含错误的表格映射发生时在其标签内显示出错符号。 以往是在保存时发现错误时显示警告信息, 比较难以发现在哪个表格映射出错。 另外不仅仅在标签显示出错符号,在筛选组中也可以筛选出 “包含错误的表格映射”,从而使修改业务 效率大幅度提高。
工作实绩细分
添加了指定工作实绩相关补充信息的工作实绩细分表格,可以对班资源成员的子任务指定实绩信息。
例如大规模组装工作等、多个工人根据各自勤务时间交替工作时、各个工人根据实绩担任了哪个时段都可以在资源甘特图表中细致的展现。
※对班资源成员反映分派需要高级选项“班资源”(2单位)。排程
[数据验证]标记违反记录
在数据验证方法可以对各个验证项目的违反记录设置标记(自由的标志)。 例如结合FLEXSCHE Editor的筛选功能可以简单筛选出出现异常的数据。
[补充生产]期间汇总补充的上弦指定&平准化
能够指定期间汇总补充所生成的补充订单的订单数量上限。 需要超过上限量的时候,生成多个补充订单。 另外可以对生成的补充订单的数量进行平准化。
[补充生产]用计算表达式定义补充汇总期间
可以用计算表达式自由定义补充汇总期间的各个日期时间。 例如“每月根据所需量汇总补充”时,月份不同汇总期间长短也不同。以往指定对各个品目设置固定的补充胡总期间,现在只要指定返回月初的日期时间列表即可实现。
[建模]基于计算表达式的选择器
被选方的选择条件可以用计算表达式设置了。例如根据生产订单的交货期自动切换工序也可以简单实现。
另外与以往的订单选择器对比时无法混用AND条件和OR条件,现在使用计算表达式可以完全自由记述了。
[建模]对应仅准备的同时堆积·并行条件
仅做准备的任务资源也可以遵守同时堆积与并行条件了。 前准备·制造·后准备的各个部分在遵守条件的基础上无误的寻找日期时间、高效安排计划。
[工作场所计划]提高间隙处理的精度
置物形状时凹多边形的时候也可以考虑间隙正确进行布局计划。
用户界面
[时序图表]任意时间段汇总显示
库存图表·负荷图表·资源滞留图表·品目滞留图表以及看板(计算表达式)中可以自由设置1天内的区分时刻、在期间内汇总显示。
[工作连接线]工作连接线的形状
在资源甘特图表和订单甘特图表可以选择除了直线以外的钩型的工作间连接线形状。
[派工图表]限定显示时间段
在派工图表可以对各个图表限定显示时间段。 例如交替勤务的时候可以只显示负责的勤务时间段。
[派工图表]各个期间分隔换页
派工图表各个期间分隔显示在打印时可以设置除了分隔期间以外的尽可能不换页。
[派工图表]用Excel打开
派工图表的显示内容可以直接用Excel打开。单元格的背景颜色和文字颜色等也完美呈现。
[窗口]窗口的标签指定颜色
各ウィンドウのタブに色を設定できるようになりました。 时序图表和FLEXSCHE Editor的表格等根据其种类显示颜色。另外可以个别对窗口指定颜色。 展开各种窗口时通过颜色可以一目了然。
[技能编辑器]技能以外也可编辑
在技能编辑器不仅时资源的技能,而且也可以编辑资源·品目·订单的数值规格了。 对多个数值规格键大量输出数据时可以通过表格形式简单输入。
计算表达式
改写列表或Map的函数
添加了为了改写列表或Map的部分值的函数。
计算表达式的记述变得更为简洁、性能得到改善す。
以往FLEXSCHE的列表或Map都有不变性(immutability)特点,一旦制作后无法改变部分值。 也就是说为了变更某列表(或Map),需要对原件进行复制返回新的列表/Map。
例如{'a', 'b', 'c', 'd'}这个列表,以变量$list引用。
以往为了在此列表的末尾添加'X'所需要的计算表达式如下。原来的列表不变、操作所复制的列表得到处理结果。
$list := $list.Append('X')
处理庞大的列表/Map的时候,进行这样的复制操作处理成本不小。 版本19可以如下记述不对原来的列表复制就能直接改写。
$list.Append_('X')
除了对末尾添加值的.Append_(),还有改写部分值的.Set_()、去除要素的.Remove_()等。
此机制在对保有变量等引用的列表或Map添加累积元素时等有显著改善性能的效果。
计算表达式的编辑支持功能
可以从项目面板简单的选择评估对象进行计算表达式的测试。另外计算表达式内记述的注释显示绿色从而提高可表达式的可读性。
保有变量的扩展
控制计算表达式的被评估时机改善性能有保有变量,此次新添加了数据的保有变量。 虽然有项目的属性这种类似的功能,但比它增加了可处理的数据类型,可以直接以变量使用,可添加说明文等,各种方面都更便于使用。
另外可以指定是否将保有变量的值保存在文件。这样从文件中读取或保存在排程中等仅一时使用的大规模列表或Map可以避免无谓的系统开销。
参数
可以对参数指定自定义数据集的记录了。 声明记录类型的参数,不用Custom('dataset').From($rec)这样的变换也可以直接传递记录了。
和外部程序的合作
添加了执行任意的程序,将其标准输出输入到计算表达式的函数Project.ConsoleCommand。 例如用数据计算库充实的python等语言进行某些运算,将其结果返回FLEXSCHE 这类使用方法可以得以实现。
另外,对外部的程序传递结构化的值时会想要以JSON格式表现数据。 新添加的函数<Map>.ToJSON / <List>.ToJSON / <Variant>.TOJSON / Variant.FromJSON、takt数据和JSON可以互换了。
将其组合,例如对外部的pytest.py文件
import sys import json import numpy as np data = json.loads( sys.argv[1] ) angle = np.deg2rad( data["angle"] ) vec = np.array(data['value']) cos = np.cos( angle ) sin = np.sin( angle ) matrix = np.array([[cos,-sin],[sin,cos]]) vec = np.dot( matrix, vec.transpose() ) print( json.dumps( vec.tolist() ) )
如此记述,在FLEXSCHE
$vec := {22, -4}, $data := { "value"=>$vec, "angle"=>45 }, $a1 := "pytest\pconv.py", $a2 := $data.ToJSON, $res := Project.ConsoleCommand( "python", { $a1, $a2 } ), Variant.FromJSON( $res )
如此进行评估,让外部程序进行矢量计算,FLEXSCHE可以读取其结果。
添加了各种函数
增加了计算表达式中的可用函数,可实现性得以进一步扩展。
其他
作为UI部件的数据立方体编辑器
能够简单开发出配有表格形式的UI迁出画面了。
可以制作出单元格可编辑的数据立方体查看器了。单元格的各个行/列是否可用、背景颜色、可否编辑等都可以通过Add-in自由控制。
例如可以制作出横轴是日期,纵轴是品目的表格,对各个日输入品目的要求量的UI。如果输入了值就从Add-in制作以该日为交货期的订单并分派工作。
※需要FLEXSCHE Analyzer的许可证
下面的Add-in脚本是开发根据成品的要求量人为调整中间材料的生产量的UI
// 此Add-in代码可以自由变更利用 // 登记GUI Add-in function SelfRegistration(addIns) { var addin; addin = addIns.AddScript("Setup", FLEXSCHE.AddInKeyHookAfterLoadingProject); addin = addIns.AddScript("Command", FLEXSCHE.AddInKeyEventFSEventFired); addin.LongValue = addIns.Application.Environment.RegisterProjectEvent( "gui.tree-node.command.invoked" ); // 制作加载项目时的DataCubeView的数据立方体 addin = addIns.AddScript( "ObtainDatacube", FLEXSCHE.AddInKeyEventFSEventFired ); addin.LongValue = addIns.Application.Environment.RegisterProjectEvent( "fse.datacube.obtain" ); // 更新所有 addin = addIns.AddScript( "AfterLoadingData", FLEXSCHE.AddInKeyHookAfterLoadingData ); addin = addIns.AddScript("Rescheduled", FLEXSCHE.AddInKeyEventFSEventFired); addin.LongValue = FLEXSCHE.FSEventRescheduled; } // 登记Editor Add-in function SelfRegistrationForFSEditor(addIns) { var addin = addIns.AddScript( "ValueChanged", FSEditor.FSEAddInKeyEventOnBodyOnDataCubeEditor ); // 单元格被编辑 => 仅更新该中间品和其下位产品 addin.SubKeyID = FSEditor.FSEAddInSubKeyEvent_Edited; } function _Setup(keyEntity) { // 项目面板的“自定义视图”节点 var project = keyEntity.ParamObject(FLEXSCHE.ParamIDProject); var str = "<?xml version='1.0'?>"; str += "<tree-node str='自定义视图' " str += "guid='{41D5879D-EF89-48C1-A681-39D57EEC5423}' type-name='top'>"; str += "<command str='添加' id='add-view' is-default='true' />"; str += "</tree-node>"; var cmd = new ActiveXObject("Microsoft.XMLDOM"); cmd.loadXML(str); project.BehaviorProperty( "#gui.project-panel.additional-tree.custom-dc" ) = cmd.documentElement; // 添加现有的子节点 var items = project.ObtainStoredData("jsobject.DCView.Items.1", []); for( var i = 0; i < items.length; i++ ) AddView(project, items[i].guid, false); return true; } // 执行树状项的命令 function _Command(keyEntity) { var project = keyEntity.ParamObject(FLEXSCHE.ParamIDProject); var nodeType = keyEntity.ParamString(FLEXSCHE.ParamIDVariant1); var guid = keyEntity.ParamString(FLEXSCHE.ParamIDVariant2); var command = keyEntity.ParamString(FLEXSCHE.ParamIDVariant3); if( nodeType == "top" && command == "add-view" ) { // 添加 var sdlUtil = project.Application.CreateCOMInstance("SDLib.SDLUtility"); var newGuid = sdlUtil.GenerateGUID(true); AddView(project, newGuid, true); var items = project.ObtainStoredData("jsobject.DCView.Items.1", []); items.push({ guid: newGuid }); project.StoredData("jsobject.DCView.Items.1") = items; } if( nodeType == "view" && command == "show-view" ) { // 显示 var dc = GetDatacube(project, guid); var fseProject = GetFSEditorProject(project); if( fseProject != null ) { var dcEditor = fseProject.ViewDataCube(dc, true); } } } // 制作加载项目时的DataCubeView的数据立方体 function _ObtainDatacube(keyEntity) { var project = keyEntity.ParamObject(FLEXSCHE.ParamIDProject); var guid = keyEntity.ParamString(FLEXSCHE.ParamIDVariant2); var items = project.ObtainStoredData("jsobject.DCView.Items.1", []); for( var i = 0; i < items.length; i++ ) { if( items[i].guid != guid ) continue; var dc = GetDatacube(project, guid); if( dc == null ) break; keyEntity.Param(FLEXSCHE.ParamIDVariant3) = dc; return true; } return false; } function GetDatacube(project, guid) { var cDCs = project.CountOfStockedObjects("datacube"); for( var iDC = 0; iDC < cDCs; iDC++ ) { var dc = project.StockedObject("datacube", iDC); if( dc != null && dc.guid == guid ) return dc; // 返回现有的数据立方体 } var sdSpace = project.DataSpace; var dc = project.Application.CreateCOMInstance("SDLib.DataCube"); dc.GUID = guid; dc.Name = "PSI-01"; project.AddStockedObject("datacube", dc); // 日期时间维度 var dimTime = dc.AddDimension("Time", SDLib.DCVTypeTime, ""); dimTime.DisplayName = "日期"; var startDate = VBDateToJDate(project.HorizonStartDate); var endDate = VBDateToJDate(project.HorizonEndDate); var exp = sdSpace.CreateTypedExpression( SData.SDVTypeTime, SData.SDVTypeString ); exp.Parse(".ToString('%^m/%^d')"); for( var date = startDate; date <= endDate; date.setDate(date.getDate() + 1) ) { var _d = VBDateFromJDate(date); var elem = dimTime.PrimitiveLayer.AddElement(_d); elem.Description = exp.CalculateFor(_d); } // 品目维度 var dimItem = dc.AddDimension("Item", SDLib.DCVTypeCustomString, "Item"); dimItem.DisplayName = "品目"; var layerItemIntermediate = dimItem.AddLayer("intermediate"); dimItem.RootLayer.AddChild(layerItemIntermediate); layerItemIntermediate.AddChild(dimItem.PrimitiveLayer); var intItems = sdSpace.Calculate("Item.Records.Select([.IsIntermediate])"); var cIntItems = intItems.Count; for( var iIntItem = 0; iIntItem < cIntItems; iIntItem++ ) { var intItemRec = intItems.ItemRec(iIntItem); var layerElem = layerItemIntermediate.AddElement(intItemRec.Code); var prodItems = sdSpace.Calculate( "Item.Records.Select([.IsFinal and .Comment('mtrl')='" + intItemRec.Code + "'])" ); var cProdItems = prodItems.Count; for( var iProdItem = 0; iProdItem < cProdItems; iProdItem++ ) { var prodItemRec = prodItems.ItemRec(iProdItem); layerElem.AddChild(dimItem.PrimitiveLayer.AddElement(prodItemRec.Code)); } } // 定义Major var mdefLimit = dc.AddMeasureElementDefinition(SDLib.DCMEDTypeNone, "Limit"); mdefLimit.ValueType = SDLib.DCVTypeDouble; mdefLimit.DisplayName = "要求上限"; var mdefRequired = dc.AddMeasureElementDefinition( SDLib.DCMEDTypeNone, "Required" ); mdefRequired.ValueType = SDLib.DCVTypeDouble; mdefRequired.DisplayName = "要求量"; var mdefProduce = dc.AddMeasureElementDefinition( SDLib.DCMEDTypeNone, "Produce" ); mdefProduce.ValueType = SDLib.DCVTypeDouble; mdefProduce.DisplayName = "生产量"; var mdefStock = dc.AddMeasureElementDefinition(SDLib.DCMEDTypeNone, "Stock"); mdefStock.ValueType = SDLib.DCVTypeDouble; mdefStock.DisplayName = "残量"; var mdefVirtual = dc.AddMeasureElementDefinition( SDLib.DCMEDTypeNone, "Virtual" ); mdefVirtual.ValueType = SDLib.DCVTypeDouble; mdefVirtual.DisplayName = "虚拟生产量"; dc.FinishToSetup(); // 控制只显示·加载 var iStock = dc.MeasureElementIndexByName("Stock"); var iProduce = dc.MeasureElementIndexByName("Produce"); var iLimit = dc.MeasureElementIndexByName("Limit"); var iVirtual = dc.MeasureElementIndexByName("Virtual"); layerItemIntermediate.MeasureElementHidden(iLimit) = true; dimItem.PrimitiveLayer.MeasureElementHidden(iStock) = true; layerItemIntermediate.MeasureElementHidden(iVirtual) = true; dimItem.PrimitiveLayer.MeasureElementHidden(iVirtual) = true; dc.ReadOnly = false; layerItemIntermediate.MeasureElementReadOnly(iStock) = true; layerItemIntermediate.MeasureElementReadOnly(iProduce) = true; dimItem.PrimitiveLayer.MeasureElementReadOnly(iLimit) = true; dimItem.PrimitiveLayer.MeasureElementReadOnly(iProduce) = true; // 将过去期间设置为只读 var roDays = sdSpace.Calculate( "TimeList.MakeInRange(project.HorizonStart,Project.SchedulingStartTime)" ); var cROs = roDays.Count; for( var iRO = 0; iRO < cROs; iRO++ ) dimTime.PrimitiveLayer.ElementByValue( roDays.Value(iRO) ) .MeasureElementReadOnly(-1) = true; UpdateValues(dc, project); return dc; } // 添加视图的树状项 function AddView(project, guid, diff) { var topElem = project.BehaviorProperty( "#gui.project-panel.additional-tree.custom-dc" ); var doc = topElem.ownerDocument; if (diff) topElem.setAttribute("sync", "true"); // <tree-node str='PSI-01' guid='4E16AE33E53440069A71A5083275BA95' // type-name='view' icon='datacube' /> var viewElem = doc.createElement("tree-node"); topElem.appendChild(viewElem); viewElem.setAttribute("str", "PSI001"); viewElem.setAttribute("guid", guid); viewElem.setAttribute("type-name", "view"); viewElem.setAttribute("icon", "datacube"); // <command str='显示' id='show-view' is-default='true' /'> var commandElem = doc.createElement("command"); viewElem.appendChild(commandElem); commandElem.setAttribute("str", "显示"); commandElem.setAttribute("id", "show-view"); commandElem.setAttribute("is-default", "true"); project.BehaviorProperty( "#gui.project-panel.additional-tree.custom-dc" ) = topElem; } function GetFSEditorProject(project) { var fse = project.Environment.AddInManager.InterfaceByProgID( "FSEditor.Entries" ); if( fse == null ) return null; var fseEnv = fse.Accessor.Environment; if( fseEnv == null ) return null; return fseEnv.Project; } /***************************************************************************************/ // 单元格的值发生变化 function _ValueChanged(keyEntity) { var editor = keyEntity.ParamObject(FSEditor.FSEParamIDDataCubeEditor); var row = keyEntity.ParamLong(FSEditor.FSEParamIDRow); var col = keyEntity.ParamLong(FSEditor.FSEParamIDCol); var locator = keyEntity.ParamObject(FSEditor.FSEParamIDDataCubeLocator); var meIndex = keyEntity.ParamLong(FSEditor.FSEParamIDLong); var measure = locator.FindMeasure(); if (measure == null) return false; var util = Script.CreateCOMInstance("SDLib.SDLUtility"); var project = editor.Project.FSProject; var sdSpace = project.DataSpace; var dc = locator.DataCube; var iTime = dc.DimensionByName("Time").IndexInDataCube; var iItem = dc.DimensionByName("Item").IndexInDataCube; var time = locator.DimensionLayerElement(iTime).Value; var itemRec = sdSpace.ItemSet.ItemRecByCode( locator.DimensionLayerElement(iItem).Value ); if (!itemRec.IsBound) return false; var meDef = dc.MeasureElementDefinition(meIndex); var qty = measure.Value(meIndex); if( qty == undefined || !util.IsDoubleNormal(qty) ) { qty = 0.0; measure.Value(meIndex) = 0.0; // 删除值后强制归0 } // 添加·更新·删除相应订单 var code = itemRec.Code + "-" + util.FormatTime(time, "%y%m%d"); var orderRec = sdSpace.OrderSet.OrderRecByCode(code); if( orderRec.IsBound ) orderRec.Qty = qty; else if( qty > 0.0 ) { orderRec = sdSpace.OrderSet.OrderRecByCode(code); if (!orderRec.IsBound) { orderRec = sdSpace.OrderSet.CreateOrderRec(SData.SDOTypeProduction, code); orderRec.ItemRec = itemRec; orderRec.AssignmentMethod = SData.SDTDirectionBackward; orderRec.Color = itemRec.Color; orderRec.LatestEndTime = time; } orderRec.Qty = qty; } // 改变相关值 var code = itemRec.Comment("mtrl"); if( code != "" ) itemRec = sdSpace.ItemSet.ItemRecByCode(code); UpdateValuesForIntermediateItem(dc, project, itemRec); // 删除不要的订单 if( orderRec.IsBound && orderRec.Qty == 0.0 ) orderRec.Delete(); editor.Project.FireEvent(FSEditor.FSEEventOrderRecsAreUpdated); } // 被重排 function _Update(keyEntity) { var project = keyEntity.ParamObject(FLEXSCHE.ParamIDProject); var views = project.views; var c = views.CountOfViews; for( var i = 0; i < c; i++ ) { var view = views.View(i); if( view.ViewType != FSVTypeCtrlView ) continue; if( view.SpecificationKey != "FSEditor.DataCubeEditor" ) continue; var dc = view.Control.DataCube; UpdateValues(dc, project); } } /***************************************************************************************/ // 更新添加到数据立方体的所有值 function UpdateValues(dc, project) { var sdSpace = project.DataSpace; if( dc == null ) return; // 清除 dc.ClearMeasures(); // 要求量 UpdateValuesForRequired(dc, project); // 生产量 UpdateValuesForProduction(dc, project, null); // 生产量调整 - 要求量调整后暂时反映未排部分 UpdateValuesForProductionAdjust(dc, project, null); // 中间品的残量和产品的要求上限 var dimItem = dc.DimensionByName("Item"); var layerItemIntermediate = dimItem.LayerByName("intermediate"); var cIntermediateElems = layerItemIntermediate.CountOfElements; for( var iIntermediateElem = 0; iIntermediateElem < cIntermediateElems; iIntermediateElem++ ) { var elem = layerItemIntermediate.ElementByIndex(iIntermediateElem); var intermediateItemRec = sdSpace.ItemSet.ItemRecByCode(elem.Value); if (!intermediateItemRec.IsBound) continue; UpdateValuesForStock(dc, project, elem, intermediateItemRec); } } // 部分更新 - 更新中间品和其相关产品的值 // 为了提效另外实施的 function UpdateValuesForIntermediateItem(dc, project, intermediateItemRec) { var sdSpace = project.DataSpace; var cItems = sdSpace.ItemSet.CountOfRecords; for( var iItem = 0; iItem < cItems; iItem++ ) { var itemRec = sdSpace.ItemSet.ItemRec(iItem); if( !itemRec.IsBound || itemRec.IsGroup) continue; if( (itemRec.Role & SData.SDIRoleIntermediate) == 0 && (itemRec.Role & SData.SDIRoleFinal) == 0 ) continue; if( itemRec.RecordKey != intermediateItemRec.RecordKey && itemRec.Comment("mtrl") != intermediateItemRec.Code ) continue; // 生产量 UpdateValuesForProduction(dc, project, itemRec); // 生产量调整 - 要求量调整后暂时反映未排部分 UpdateValuesForProductionAdjust(dc, project, itemRec); } // 中间品的残量和产品的要求上限 var dimItem = dc.DimensionByName("Item"); var layerItemIntermediate = dimItem.LayerByName("intermediate"); var elem = layerItemIntermediate.ElementByValue(intermediateItemRec.Code); UpdateValuesForStock(dc, project, elem, intermediateItemRec); } // 要求量 - 收集所有的订单的数量 function UpdateValuesForRequired(dc, project) { var iRequired = dc.MeasureElementIndexByName("Required"); var expFloor = project.DataSpace.CreateTypedExpression( SData.SDVTypeTime, SData.SDVTypeTime ); expFloor.Parse(".Floor"); var layerTime = dc.DimensionByName("Time").PrimitiveLayer; var layerIntermediate = dc .DimensionByName("Item") .LayerByName("intermediate"); var layerProduct = dc.DimensionByName("Item").PrimitiveLayer; var loc = dc.CreateLocator(); var orderSet = project.DataSpace.OrderSet; var cOrders = orderSet.CountOfRecords; for( var iOrder = 0; iOrder < cOrders; iOrder++ ) { var orderRec = orderSet.OrderRec(iOrder); var itemRec = orderRec.ItemRec; var qty = orderRec.Qty; var time = expFloor.CalculateFor(orderRec.LatestEndTime); loc.SetDimensionLayerElement(layerTime.ElementByValue(time)); var elem = itemRec.Role & SData.SDIRoleIntermediate ? layerIntermediate.ElementByValue(itemRec.Code) : layerProduct.ElementByValue(itemRec.Code); loc.SetDimensionLayerElement(elem); loc.ObtainMeasure().Value(iRequired) = qty; } } // 生产量 - 无法特定品目时对全品目 function UpdateValuesForProduction(dc, project, targetItemRec) { var sdSpace = project.DataSpace; var itemSet = sdSpace.ItemSet; var cItems = itemSet.CountOfRecords; var layerTime = dc.DimensionByName("Time").PrimitiveLayer; var layerIntermediate = dc.DimensionByName("Item").LayerByName("intermediate"); var layerProduct = dc.DimensionByName("Item").PrimitiveLayer; var iProduce = dc.MeasureElementIndexByName("Produce"); var loc = dc.CreateLocator(); var exp = project.DataSpace.CreateTypedExpression( SData.SDVTypeItemRec, SData.SDVTypeVariantList ); exp.Parse( "TimeList.MakeInRange(Project.HorizonStart,Project.HorizonEnd)" + ".Convert([$$_object.InventorySubtotalQty(InventoryPCType" +".Produce,$_object,$_object+#P1D#,false)])" ); for( var iItem = 0; iItem < cItems; iItem++ ) { var itemRec = targetItemRec == null ? itemSet.ItemRec(iItem) : targetItemRec; if( itemRec.IsGroup ) continue; var layer = itemRec.Role & SData.SDIRoleIntermediate ? layerIntermediate : layerProduct; var itemElem = layer.ElementByValue(itemRec.Code); if( itemElem == null ) continue; loc.SetDimensionLayerElement(itemElem); var qtyList = exp.CalculateFor(itemRec); // 保存在数据立方体 var c = qtyList.Count; for( var i = 0; i < c; i++ ) { loc.SetDimensionLayerElement(layerTime.ElementByIndex(i)); loc.ObtainMeasure().Value(iProduce) = qtyList.Value(i); } if( targetItemRec != null ) break; } } // 生产量调整 - 要求量调整后暂时反映未排部分 function UpdateValuesForProductionAdjust(dc, project, targetItemRec) { var sdSpace = project.DataSpace; var orderSet = sdSpace.OrderSet; var cOrders = orderSet.CountOfRecords; var expQty = sdSpace.CreateTypedExpression( SData.SDVTypeOrderRec, SData.SDVTypeDouble ); expQty.Parse( ".Qty" + " - ( ( .Operation.DoesExist and .Operation.IsAssigned ) ? .Operation.LinkQty : 0.0 )" ); var expIndex = sdSpace.CreateTypedExpression( SData.SDVTypeOrderRec, SData.SDVTypeLong ); expIndex.Parse("( .LatestEndTime - Project.HorizonStart ).Days"); var loc = dc.CreateLocator(); var layerTime = dc.DimensionByName("Time").PrimitiveLayer; var layerIntermediate = dc.DimensionByName("Item").LayerByName("intermediate"); var layerProduct = dc.DimensionByName("Item").PrimitiveLayer; var iProduce = dc.MeasureElementIndexByName("Produce"); var iVirtual = dc.MeasureElementIndexByName("Virtual"); // 指定品目时清楚虚拟生产量 if( targetItemRec != null ) { var layer = targetItemRec.Role & SData.SDIRoleIntermediate ? layerIntermediate : layerProduct; loc.SetDimensionLayerElement(layer.ElementByValue(targetItemRec.Code)); loc.SetMeasureValues(iVirtual, 0.0); // まとめてクリア } // 扫描订单 for (var iOrder = 0; iOrder < cOrders; iOrder++) { var orderRec = orderSet.OrderRec(iOrder); if( !orderRec.IsBound ) continue; var itemRec = orderRec.ItemRec; if( targetItemRec != null && itemRec.RecordKey != targetItemRec.RecordKey ) continue; var qty = expQty.CalculateFor(orderRec); if( qty == 0.0 ) continue; var index = expIndex.CalculateFor(orderRec); if( index < 0 ) continue; var layer = itemRec.Role & SData.SDIRoleIntermediate ? layerIntermediate : layerProduct; var itemElem = layer.ElementByValue(itemRec.Code); if( itemElem == null ) continue; loc.SetDimensionLayerElement(itemElem); loc.SetDimensionLayerElement(layerTime.ElementByIndex(index)); loc.ObtainMeasure().Value(iVirtual) = qty; var v = loc.ObtainMeasure().Value(iProduce); loc.ObtainMeasure().Value(iProduce) = v == undefined ? qty : qty + v; } } // 中间品的残量和产品的要求上限 function UpdateValuesForStock( dc, project, intermediateElem, intermediateItemRec ) { var sdSpace = project.DataSpace; var sdlUtil = project.Application.CreateCOMInstance("SDLib.SDLUtility"); var exp = sdSpace.CreateTypedExpression( SData.SDVTypeItemRec, SData.SDVTypeVariantList ); exp.Parse( "TimeList.MakeInRange(Project.HorizonStart,Project.HorizonEnd)" + ".Convert([$$_object.InventoryQty($_object,false)])" ); var qtyList = exp.CalculateFor(intermediateItemRec); var layerTime = dc.DimensionByName("Time").PrimitiveLayer; var cTimes = layerTime.CountOfElements; var loc = dc.CreateLocator(); var iStock = dc.MeasureElementIndexByName("Stock"); var iVirtual = dc.MeasureElementIndexByName("Virtual"); var iLimit = dc.MeasureElementIndexByName("Limit"); var iRequired = dc.MeasureElementIndexByName("Required"); // 子产品列表 var children = []; var cChildren = intermediateElem.CountOfChildren; for( var iChild = 0; iChild < cChildren; iChild++ ) { var child = intermediateElem.Child(iChild); var itemRec = sdSpace.ItemSet.ItemRecByCode(child.Value); var ratio = itemRec.SpecCollection.NumSpec("ratio"); ratio = sdlUtil.IsDoubleNormal(ratio) ? ratio : 1.0; children.push({ child: child, ratio: ratio }); } // 中间品残量 var virtualQty = 0.0; loc.SetDimensionLayerElement(intermediateElem); var locChild = dc.CreateLocator(); for( var iTime = 0; iTime < cTimes; iTime++ ) { var timeElem = layerTime.ElementByIndex(iTime); loc.SetDimensionLayerElement(timeElem); locChild.SetDimensionLayerElement(timeElem); virtualQty += loc.ObtainMeasure().Value(iVirtual); var cChildren = children.length; for (var iChild = 0; iChild < cChildren; iChild++) { var v = children[iChild]; locChild.SetDimensionLayerElement(v.child); virtualQty -= locChild.ObtainMeasure().Value(iVirtual) * v.ratio; } loc.ObtainMeasure().Value(iStock) = qtyList.Value(iTime) + virtualQty; } // 产品的要求上限 var cChildren = children.length; for( var iChild = 0; iChild < cChildren; iChild++ ) { var v = children[iChild]; locChild.SetDimensionLayerElement(v.child); var futureMin = 9999999.9; for( var iTime = cTimes - 1; iTime >= 0; iTime-- ) { var timeElem = layerTime.ElementByIndex(iTime); loc.SetDimensionLayerElement(timeElem); locChild.SetDimensionLayerElement(timeElem); var qty = loc.MeasureValue(iStock); if( futureMin > qty ) futureMin = qty; var req = locChild.MeasureValue(iRequired); var q = Math.floor(futureMin / v.ratio) + req; locChild.ObtainMeasure().Value(iLimit) = q < 0.0 ? 0.0 : q; } } } /***************************************************************************************/ // generated by WSCGEN function Setup(keyEntity) { ret = _Setup(keyEntity); CollectGarbage(); return ret; } function Command(keyEntity) { ret = _Command(keyEntity); CollectGarbage(); return ret; } function ObtainDatacube(keyEntity) { ret = _ObtainDatacube(keyEntity); CollectGarbage(); return ret; } function ValueChanged(keyEntity) { ret = _ValueChanged(keyEntity); CollectGarbage(); return ret; } function Rescheduled(keyEntity) { ret = _Update(keyEntity); CollectGarbage(); return ret; } function AfterLoadingData(keyEntity) { ret = _Update(keyEntity); CollectGarbage(); return ret; } function VBDateFromJDate(d) { return Script.VBDateFromSeconds( d.getTime() / 1000 - d.getTimezoneOffset() * 60 ); } function VBDateFromLiteral(str) { var d = new Date(str); return Script.VBDateFromSeconds( d.getTime() / 1000 - d.getTimezoneOffset() * 60 ); } function VBDateToJDate(t) { d = new Date(); d.setTime(Script.VBDateToSeconds(t) * 1000 + d.getTimezoneOffset() * 60000); return d; } function alert(msg) { Script.ShowMessageBox(msg, "FLEXSCHE", AIM.MBTOk); }
Communicator上的d-MPS以品目单位签出
Communicator的项目使用FLEXSCHE d-MPS的时候,能够以品目记录单位签出编辑MPS数据了。 以前需要签出所有数据,1次之能1个计划员进行编辑。现在不同计划员负责不同品目的时候可以同时编辑了。