diff --git a/BPASmartClient.MORKSM.BK.PLC/PLCMachine.cs b/BPASmartClient.MORKSM.BK.PLC/PLCMachine.cs index a14a0740..657783a2 100644 --- a/BPASmartClient.MORKSM.BK.PLC/PLCMachine.cs +++ b/BPASmartClient.MORKSM.BK.PLC/PLCMachine.cs @@ -16,6 +16,8 @@ namespace BPASmartClient.PLC { Task.Run(new Action(() => { modbusTcp.ModbusTcpConnect(communicationPar.IPAddress, communicationPar.IPPort); })); //PLC 设备连接 + Thread.Sleep(1000); + ThreadManage.GetInstance().StartLong(new Action(() => { IsConnected = modbusTcp.Connected; @@ -41,7 +43,7 @@ namespace BPASmartClient.PLC Thread.Sleep(500); } Thread.Sleep(1000); - }), $"设备[{DeviceId}]PLC读取线程", true); + }), $"设备[{DeviceId}][{modbusTcp.IPAdress}]PLC读取线程", true); //写入数据 EventBus.EventBus.GetInstance().Subscribe(DeviceId, delegate (IEvent @event, EventCallBackHandle callBack) diff --git a/BPASmartClient.MorkF/Control_MorkF.cs b/BPASmartClient.MorkF/Control_MorkF.cs index 4469134d..6bbdbbd5 100644 --- a/BPASmartClient.MorkF/Control_MorkF.cs +++ b/BPASmartClient.MorkF/Control_MorkF.cs @@ -39,8 +39,16 @@ namespace BPASmartClient.MorkF public AutoResetEvent minorReset = new AutoResetEvent(false); public AutoResetEvent mainReset = new AutoResetEvent(false); + /// + /// 炒锅编号与炒锅实例 + /// Dictionary morkFs = new Dictionary();//全局对象声明 + /// + /// 菜品库对象 + /// + ML_MorkF ml_morkf = new ML_MorkF(); + /// /// 小炒菜单集合 /// @@ -51,6 +59,9 @@ namespace BPASmartClient.MorkF /// private ConcurrentQueue StirFryGoodsQuenes = new ConcurrentQueue(); + /// + /// 炒锅炒制线程名称 + /// private const String striConst = "炒锅{0}炒制{1}线程"; /// @@ -65,43 +76,100 @@ namespace BPASmartClient.MorkF /// public override void DoMain() { - - WriteControl("VD836", 0); - WriteControl("VD840", 0); - Thread.Sleep(400); - WriteControl("M0.0", true); + IsHealth = true; + //WriteControl("VD836", 0); + //WriteControl("VD840", 0); + //Thread.Sleep(400); + //WriteControl("M0.0", true); for (int i = 0;i< count;i++) { morkFs.Add(i, new GVL_MorkF()); - DataParse(i);//数据解析 } + DataParse();//数据解析 CommandRegist();//调试 ServerInit(); DeviceProcessLogShow("MORKF 设备初始化完成"); + } + + /// + /// 主任务 + /// + public override void MainTask() + { + MainProcessExcute(); + //MinorProcessExcute(); + //SingleProcess(); + } - ReadData(); + public override void Stop() + { + IsHealth = false; } + #region 调试代码 public void CommandRegist() { #region 设备控制 ActionManage.GetInstance.Register(PLCInite, "InitCommand"); ActionManage.GetInstance.Register(StartOrder, "StartOrder"); + ActionManage.GetInstance.Register(StartOrderMain, "开始下单"); ActionManage.GetInstance.Register(StartLocalOrder, "StartLocalOrder"); ActionManage.GetInstance.Register(StopLocalOrder, "StopLocalOrder"); #endregion #region 菜品库 - ActionManage.GetInstance.Register(FoodLibInit, "FoodLibInit"); - ActionManage.GetInstance.Register(Electromagnetism, "Electromagnetism"); - ActionManage.GetInstance.Register(GetDistance_1, "GetDistance_1"); - ActionManage.GetInstance.Register(GetDistance_2, "GetDistance_2"); - ActionManage.GetInstance.Register(GetDistance_3, "GetDistance_3"); + ActionManage.GetInstance.Register(new Action(() => + { + ThreadManage.GetInstance().Start(new Action(() => + { + FoodLibInit(); + }), "FoodLibInit"); + }), "FoodLibInit"); + ActionManage.GetInstance.Register(new Action((o) => + { + ThreadManage.GetInstance().Start(new Action(() => + { + Electromagnetism(o); + }), "Electromagnetism"); + }), "Electromagnetism"); + ActionManage.GetInstance.Register(new Action(() => + { + ThreadManage.GetInstance().Start(new Action(() => + { + GetDistance_1(); + }), "GetDistance_1"); + }), "GetDistance_1"); + ActionManage.GetInstance.Register(new Action(() => + { + ThreadManage.GetInstance().Start(new Action(() => + { + GetDistance_2(); + }), "GetDistance_2"); + }), "GetDistance_2"); + ActionManage.GetInstance.Register(new Action(() => + { + ThreadManage.GetInstance().Start(new Action(() => + { + GetDistance_3(); + }), "GetDistance_3"); + }), "GetDistance_3"); ActionManage.GetInstance.Register(PawTurnFront, "PawTurnFront"); ActionManage.GetInstance.Register(PawTurnBack, "PawTurnBack"); - ActionManage.GetInstance.Register(SetArmPosition, "SetArmPosition"); + ActionManage.GetInstance.Register(new Action((list) => + { + ThreadManage.GetInstance().Start(new Action(() => + { + if(list is List list_int) + { + if (list_int.Count == 2) + { + SetArmPosition(list_int[0], list_int[1]); + } + } + }), "SetArmPosition"); + }), "SetArmPosition"); #endregion //ActionManage.GetInstance.Register(PLCInite, "InitCommand"); #region 配料控制 @@ -115,7 +183,7 @@ namespace BPASmartClient.MorkF //ActionManage.GetInstance.Register(OutSeasoning, "OutMaterials"); #endregion - #region 炒锅1 + #region 炒锅 ActionManage.GetInstance.Register(new Action(() => { ThreadManage.GetInstance().Start(new Action(() => @@ -232,519 +300,890 @@ namespace BPASmartClient.MorkF } - #region 菜品库 - /// - /// 菜品库数据写入 - /// - /// - /// - private void MaterailLibrary_Write(string address,object value) - { - WriteControlExact(address, value, 1); - } /// - /// 菜品库初始化 + /// 本地菜单下单 /// - public void FoodLibInit() + private void StartOrder(object o) { - MaterailLibrary_Write("", true); + if(o==null) return; + if(o is int goodId) + { + var res = LocalstirFryGoods?.FirstOrDefault(p => p.GoodsKey == goodId);//匹配订单对应制作流程 + if (res != null) + { + /* morkF.listStirBom.Add(res.StirFryBomInfo);*///添加订单制作流程 + if (StirFryGoodsQuenes.Count > 0) return; + StirFryGoodsQuenes.Enqueue(new OrderLocInfo() + { + SuborderId = Guid.NewGuid().ToString(), + StirPotActions = res.StirPotActions, + GoodName = "本地菜品" + }); + MessageLog.GetInstance.Show($"添加本地订单{res.GoodsKey}"); + } + } } + /// - /// 电磁阀启停 + /// 本地菜单下单 /// - /// - public void Electromagnetism(object o) + private void StartOrderMain(object o) { if (o == null) return; - if (o is List bs && bs.Count == 1) + if (o is string goodId) { - MaterailLibrary_Write("", bs[0]); + var res = LocalstirFryGoods?.FirstOrDefault(p => p.GoodsKey.ToString() == goodId);//匹配订单对应制作流程 + if (res != null) + { + /* morkF.listStirBom.Add(res.StirFryBomInfo);*///添加订单制作流程 + if (StirFryGoodsQuenes.Count > 0) return; + StirFryGoodsQuenes.Enqueue(new OrderLocInfo() + { + SuborderId = Guid.NewGuid().ToString(), + StirPotActions = res.StirPotActions, + GoodName = "本地菜品" + }); + MessageLog.GetInstance.Show($"添加本地订单{res.GoodsKey}"); + } } } - public void GetDistance_1() - { - MaterailLibrary_Write("", true); - } - public void GetDistance_2() - { - MaterailLibrary_Write("", true); - } - public void GetDistance_3() - { - MaterailLibrary_Write("", true); - } - public void PawTurnFront() + + private void StartLocalOrder() { - MaterailLibrary_Write("", true); + if (StirFryGoodsQuenes.Count > 0) return;//只能一个一个做 + if (Json.Data.LocalstirFryGoods.StirPotActions.Count>0) + { + StirFryGoodsQuenes.Enqueue(new OrderLocInfo() + { + SuborderId = Guid.NewGuid().ToString(), + StirPotActions = Json.Data.LocalstirFryGoods.StirPotActions, + GoodName = "本地菜品" + }); + MessageLog.GetInstance.Show($"添加本地模拟的订单{Json.Data.LocalstirFryGoods.GoodsKey}"); + } } - public void PawTurnBack() + + /// + /// 停止本地菜单炒制 + /// + private void StopLocalOrder(int num = -1) { - MaterailLibrary_Write("", true); + //判断当前是否有炒制菜品 + if (nowStirFryGood == null) return; + + //根据morkFs中是否有num执行不同的停止操作 + if (!morkFs.ContainsKey(num)) + { + //根据调试界面的当前炒锅编号停止炒制线程 + ThreadManage.GetInstance().StopTask(String.Format(striConst, fryIndex.ToString(), nowStirFryGood.GoodName), new Action(() => { Plc1Reset(fryIndex); })); + } + else + { + //根据炒锅编号停止炒制线程 + ThreadManage.GetInstance().StopTask(String.Format(striConst, fryIndex.ToString(), nowStirFryGood.GoodName), new Action(() => { Plc1Reset(num); })); + } } + /// - /// 设定机械臂的位置 + /// 重置程序 /// - /// - /// - public void SetArmPosition(int X, int y) + public override void ResetProgram() { - MaterailLibrary_Write("", true); - } - #endregion + IsHealth = true; + morkFs.Clear(); + morkFs = new Dictionary(); + ml_morkf = new ML_MorkF(); + //根据编号new炒锅实例对象 + for (int i = 0; i < count; i++) + { + morkFs.Add(i, new GVL_MorkF()); + } + } + #endregion + #region 公用PLC方法 /// - /// 出调料 + /// 获取设备PLC的所有状态 /// - /// - public void OutSeasoning(object o,int num) + /// + /// + /// 炒锅编号 + private void GetStatus(string key, Action action, int num) { - if (o == null) return; - if (o is List ints && ints.Count == 2&&morkFs.ContainsKey(num)) + if (dicPort2peripheralStatus.ContainsKey(num)) { - FirePot1_Write(morkFs[num].PassWayValue[ints[0]], (ushort)ints[1],num);//写入通道值 - Thread.Sleep(400); - FirePot1_Write(morkFs[num].StartPassWay[ints[0]], true, num);//开启通道 - Thread.Sleep(400); - FirePot1_Write(morkFs[num].StartPassWay[ints[0]], false, num);//开启通道 + if (dicPort2peripheralStatus[num].ContainsKey(key)) + { + action((object)dicPort2peripheralStatus[num][key]);//获取PLC指定地址的状态值 + } } } + /// - /// 出多个调料 + /// PLC数据读取 /// - public void OutSeasonings(List seasoningLists,int num) + public override void ReadData() { - //防止越界 - if(!morkFs.ContainsKey(num)) - { - return; - } - foreach (SeasoningList seasoning in seasoningLists) + for (int i = 0; i < morkFs.Count; i++) { - FirePot1_Write(morkFs[num].PassWayValue[seasoning.Loc], (ushort)seasoning.Qty, num); - Thread.Sleep(300); + GetStatus("LB50", new Action((objects) => + { + if (!morkFs.ContainsKey(i)) + { + return; + } + if (objects is bool[] bools) + { + morkFs[i].FryPot1_InitialComplete = bools[0]; + morkFs[i].FryPot1_HOBTPut = bools[1]; + morkFs[i].FryPot1_HOBTGet = bools[2]; + morkFs[i].FryPot1_MaterialIntoPot = bools[3]; + morkFs[i].OutFoodCompelete = bools[4]; + morkFs[i].CanOutFood = bools[5]; + morkFs[i].GetFoodCompelete = bools[6]; + morkFs[i].CanOutPotWashingWater = bools[7]; + morkFs[i].ArmOnOrigin = bools[8]; + morkFs[i].ArmOnWorking = bools[9]; + morkFs[i].PotOnOrigin = bools[10]; + } + + }), i); } - foreach (SeasoningList seasoning in seasoningLists) + for (int j = 0; j < morkFs.Count; j++) { - FirePot1_Write(morkFs[num].StartPassWay[seasoning.Loc], true, num); - Thread.Sleep(300); + GetStatus("LB74", new Action((objects) => + { + if (!morkFs.ContainsKey(j)) + { + return; + } + + if (objects is bool[] bools) + { + for (int i = 0; i < 14; i++) + { + morkFs[j].PassWay1_Compelete[i] = bools[i]; + } + } + + }), j); } - foreach (SeasoningList seasoning in seasoningLists) + for (int i = 0; i < morkFs.Count; i++) { - FirePot1_Write(morkFs[num].StartPassWay[seasoning.Loc], false, num); - Thread.Sleep(300); + if (!morkFs.ContainsKey(i)) + { + return; + } + + GetStatus("LB90", new Action((objects) => + { + if (objects is bool[] bools) + { + morkFs[i].AutoMode = bools[0]; + } + + }), i); } - foreach (SeasoningList seasoning in seasoningLists) + //获取激光距离 + GetStatus("VW270", new Action((objects) => { - FirePot1_Write(morkFs[num].StartPassWay[seasoning.Loc], false, num); - Thread.Sleep(300); - } - } + if (objects is int[] bools) + { + for (int i = 0; i < 1; i++) + { + ml_morkf.LaserDistance = bools[i]; + } + } + }), 2); - #region 炒锅1 - private void FirePot1_Write(string address,object value,int num) - { - WriteControlExact(address, value, num); - } - /// - /// 复位 - /// - public void Plc1Reset(int num) - { - ThreadManage.GetInstance().Start(new Action(() => + //获取坐标X + GetStatus("VD828", new Action((objects) => { - StopFire(num); - Thread.Sleep(200); - StopStir(num); - Thread.Sleep(200); - FirePot1_Write("LB5", false, num); - Thread.Sleep(200); - FirePot1_Write("LB3", false, num); - Thread.Sleep(200); - FirePot1_Write("LB6", false, num); - Thread.Sleep(200); - FirePot1_Write("LB7", false, num); - Thread.Sleep(200); - FirePot1_Write("LB4", false, num); - Thread.Sleep(200); - FirePot1_Write("LB53", false, num); - if(morkFs.ContainsKey(num)) + if (objects is int[] bools) { - foreach (var item in morkFs[num].StartPassWay.Values) + for (int i = 0; i < 1; i++) { - Thread.Sleep(200); - FirePot1_Write(item, false, num); + ml_morkf.ArmPositionX = bools[i]; } } - }),"炒锅1初始化"); - - } - //加油 - public void AddOil() - { + }), 2); - } - //加热启动 - public void StartFire(int num) - { - FirePot1_Write("LB1", true, num); - Thread.Sleep(200); - } - //加热停止 - public void StopFire(int num) + //获取坐标Y + GetStatus("VD832", new Action((objects) => + { + if (objects is int[] bools) + { + for (int i = 0; i < 1; i++) + { + ml_morkf.ArmPositionY = bools[i]; + } + } + }), 2); + + //获取爪子到达位置 + GetStatus("M11.0", new Action((objects) => + { + if (objects is bool[] bools && bools.Length > 3) + { + + //ml_morkf.PawArrivePortOne = bools[0]; + //ml_morkf.PawArrivePortTwo = bools[1]; + //ml_morkf.PawArrivePortThree = bools[2]; + + if (isInitialPaw == false) + { + isInitialArrive = true; + } + else + { + //小炒爪子到达1号位上升沿信号捕获 + if (RTrig.GetInstance("SmartGitdistance1Complete").Start(bools[0])) + { + ml_morkf.PawArrivePortOne = true; + } + //小炒爪子到达3号位上升沿信号捕获 + if (RTrig.GetInstance("SmartGitdistance2Complete").Start(bools[1])) + { + ml_morkf.PawArrivePortTwo = true; + } + //小炒爪子到达3号位上升沿信号捕获 + if (RTrig.GetInstance("SmartGitdistance3Complete").Start(bools[2])) + { + ml_morkf.PawArrivePortThree = true; + } + isInitialArrive = true; + } + } + }), 2); + + //获取定位到达状态 + GetStatus("M10.0", new Action((objects) => + { + if (objects is bool[] bools) + { + for (int i = 0; i < 1; i++) + { + //ml_morkf.ArriveComplete = bools[i]; + //小炒定点到达上升沿信号捕获 + if (RTrig.GetInstance("SmartArriveComplete").Start(bools[i])) + { + if (isInitialArrive == false) + { + MessageLog.GetInstance.Show("到达上升沿为true,isInitialArrive is false"); + isInitialArrive = true; + } + else + { + MessageLog.GetInstance.Show("到达上升沿为true"); + ml_morkf.ArriveComplete = true; + isInitialArrive = true; + } + } + } + } + + }), 2); + + //获取总初始化完成 + GetStatus("M10.2", new Action((objects) => + { + if (objects is bool[] bools) + { + for (int i = 0; i < 1; i++) + { + ml_morkf.InitialComplete = bools[i]; + } + } + }), 2); + + //抓手初始化完成 + GetStatus("M10.3", new Action((objects) => + { + if (objects is bool[] bools) + { + for (int i = 0; i < 1; i++) + { + ml_morkf.PawInitialComplete = bools[i]; + } + } + }), 2); + + } + + #endregion + + #region 菜品库PLC操作方法 + /// + /// 菜品库数据写入 + /// + /// + /// + private void MaterailLibrary_Write(string address, object value,int num) { - FirePot1_Write("LB1", false, num); - Thread.Sleep(200); + WriteControlExact(address, value, num); } - //搅拌启动 - public void StartStir(int num) + /// + /// 菜品库初始化 + /// + public void FoodLibInit(int num = 2) { - FirePot1_Write("LB2", true, num); - Thread.Sleep(200); + MaterailLibrary_Write("M0.2", true, num); } - //搅拌启停止 - public void StopStir(int num) + /// + /// 电磁阀启停 + /// + /// 电磁阀启停:true:启动。false:停止。 + public void Electromagnetism(object o,int num = 2) { - FirePot1_Write("LB2", false, num); - Thread.Sleep(200); + if (o == null) return; + if (o is List bs && bs.Count == 1) + { + MaterailLibrary_Write("M1.3", bs[0],num); + } } - //倒菜 - public void OutFood(int num) + public bool GetDistance_1(int num = 2) { - if(!morkFs.ContainsKey(num)) + MessageLog.GetInstance.Show("爪子去到1号位"); + MaterailLibrary_Write("M1.0", true, num); + for (int i = 0; i < sleepCount && !ml_morkf.PawArrivePortOne; i++) { - return; + Thread.Sleep(sleepTime); + if (i >= sleepCount - 1) + { + MessageLog.GetInstance.Show("爪子去到1号位超时"); + return false; + } } - - FirePot1_Write("LB3", true, num); - MessageLog.GetInstance.Show("倒菜启动"); - - Thread.Sleep(400); - - for (int i = 0; i < sleepCount && !morkFs[num].FryPot1_MaterialIntoPot; i++) + MessageLog.GetInstance.Show("爪子去到1号位完成"); + return true; + } + public bool GetDistance_2(int num = 2) + { + MessageLog.GetInstance.Show("爪子去到2号位"); + MaterailLibrary_Write("M1.1", true, num); + for (int i = 0; i < sleepCount && !ml_morkf.PawArrivePortTwo; i++) { Thread.Sleep(sleepTime); if (i >= sleepCount - 1) { - MessageLog.GetInstance.Show($"炒锅{num}倒菜超时"); + MessageLog.GetInstance.Show("爪子去到2号位超时"); + return false; } } - - FirePot1_Write("LB3", false, num); - Thread.Sleep(200); - MessageLog.GetInstance.Show("倒菜完成"); + MessageLog.GetInstance.Show("爪子去到2号位完成"); + return true; } - //搅拌臂去原点位 - public void StirArmGoOrigin(int num) + public bool GetDistance_3(int num = 2) { - if (!morkFs.ContainsKey(num)) + MessageLog.GetInstance.Show("爪子去到3号位"); + MaterailLibrary_Write("M1.2", true,num); + for (int i = 0; i < sleepCount && !ml_morkf.PawArrivePortThree; i++) { - return; + Thread.Sleep(sleepTime); + if (i >= sleepCount - 1) + { + MessageLog.GetInstance.Show("爪子去到3号位超时"); + return false; + } } + MessageLog.GetInstance.Show("爪子去到3号位完成"); + return true; + } + public void PawTurnFront(int num = 2) + { + MaterailLibrary_Write("", true, num); + } + public void PawTurnBack(int num = 2) + { + MaterailLibrary_Write("", true, num); + } - FirePot1_Write("LB5", true, num); - MessageLog.GetInstance.Show("搅拌臂去原点位"); + /// + /// 设定机械臂的位置 + /// + /// + /// + public bool SetArmPosition(int x, int y, int num = 2) + { + //取反 + x -= 2 * x; + y -= 2 * y; + MaterailLibrary_Write("VD836", x, num); + Thread.Sleep(100); + MaterailLibrary_Write("VD840", y, num); - for (int i = 0; i < sleepCount && !morkFs[num].ArmOnOrigin; i++) + Thread.Sleep(200); + + MessageLog.GetInstance.Show($"机械臂移动到[{x},{y}]"); + //定位启动 + MaterailLibrary_Write("M0.0", true, num); + + for (int i = 0; i < sleepCount && !ml_morkf.ArriveComplete; i++) { Thread.Sleep(sleepTime); if (i >= sleepCount - 1) { - MessageLog.GetInstance.Show($"炒锅{num}搅拌臂去原点位超时"); + MessageLog.GetInstance.Show("机械臂移动操作超时"); + return false; } } - //while (!morkFs[num].ArmOnOrigin) - //{ - // Thread.Sleep(200); - //} - FirePot1_Write("LB5", false, num); - Thread.Sleep(200); - MessageLog.GetInstance.Show("搅拌臂到达原点位"); + MessageLog.GetInstance.Show("机械臂移动操作完成"); - + Thread.Sleep(500); + return true; } - //搅拌臂去炒制位 - public void StirArmGoWork(int num) + //菜品库锁 + object lock_Materail = new object(); + + private const int up = 10000; + + /// + /// 获取菜品 + /// + public void GetMaterail(int x,int y) { - if (!morkFs.ContainsKey(num)) + //设置到抓菜处 + if(!SetArmPosition(x, y)) { return; } - if (!morkFs[num].ArmOnWorking/* && morkFs[num].PotOnOrigin*/) + //Thread.Sleep(2000); + + //关闭电磁阀 + Electromagnetism(new List { true }); + Thread.Sleep(500); + + //爪子去1号位抓菜 + if (!GetDistance_1()) { - FirePot1_Write("LB6", true, num); - MessageLog.GetInstance.Show("搅拌臂去工作位"); + return; + } + Thread.Sleep(500); - for (int i = 0; i < sleepCount && !morkFs[num].ArmOnWorking; i++) + //移动前开启电磁阀 + Electromagnetism(new List { false }); + Thread.Sleep(500); + + //设置到出菜处,都在x轴-40000处出菜 + if (!SetArmPosition(-40000, y)) + { + return; + } + + Thread.Sleep(500); + //爪子去3号位放菜 + if (!GetDistance_3()) + { + return; + } + Thread.Sleep(500); + ////关闭电磁阀 + //Electromagnetism(new List { false }); + //Thread.Sleep(300); + } + + /// + /// 获取菜品测试 + /// + /// + private void GetMaterailTest(int getNum) + { + ThreadManage.GetInstance().Start(new Action(()=>{ + int x1 = 0, x2 = 0, y= 0; + + for (int i = 0; i < sleepCount * 2 && ml_morkf.MaterailIsWorking; i++) { Thread.Sleep(sleepTime); - if (i >= sleepCount - 1) + if (i >= sleepCount * 2 - 1) { - MessageLog.GetInstance.Show($"炒锅{num}搅拌臂去炒制位超时"); + MessageLog.GetInstance.Show("菜品库正在工作中....."); + return; } } - //while (!morkFs[num].ArmOnWorking) + //if (!ml_morkf.InitialComplete) //{ - // Thread.Sleep(200); + // MessageLog.GetInstance.Show("菜品库未初始化"); + // return; //} + if (getNum == 0) + { + x2 = -20000; + y = 40000; - FirePot1_Write("LB6", false, num); - Thread.Sleep(200); - MessageLog.GetInstance.Show("搅拌臂到达工作位"); - } + } + else if (getNum == 1) + { + x2 = -15000; + y = -52500; + } + else + { + MessageLog.GetInstance.Show("菜品库无效操作"); + return; + } + try + { + lock (lock_Materail) + { + ml_morkf.MaterailIsWorking = true; + } + //菜品库去指定位置 + if (!SetArmPosition(x1, y)) + { + MessageLog.GetInstance.Show("菜品库[机械臂操作失败]"); + return; + } + //取菜操作 + GetMaterail(x2,y); + + + //机械臂去取菜放位 + if (!SetArmPosition(-30000, -20000)) + { + MessageLog.GetInstance.Show("菜品库[机械臂操作失败]"); + return; + } + //机械臂回原点 + if (!SetArmPosition(0, 0)) + { + MessageLog.GetInstance.Show("菜品库[机械臂操作失败]"); + return; + } + } + catch (Exception ex) + { + + } + finally + { + lock (lock_Materail) + { + ml_morkf.MaterailIsWorking = false; + } + } + + }),$"菜品库操作{getNum}"); } - //HBOT放盒子到位 - public void HBOTGoWork(int num) + #endregion + + + #region 炒锅PLC基本操作方法 + /// + /// 炒锅写寄存器方法,num为炒锅对应编号,从0开始 + /// + /// 寄存器地址 + /// 值 + /// 炒锅编号 + private void FirePot_Write(string address, object value, int num) { - FirePot1_Write("LB7", true, num); - Thread.Sleep(400); - FirePot1_Write("LB7", false, num); + WriteControlExact(address, value, num); } - //出餐启动 - public void OutMeal(int num) + + /// + /// 出调料 + /// + /// + public void OutSeasoning(object o, int num) { - if (!morkFs[num].ArmOnOrigin /*&& morkFs[num].PotOnOrigin*/) + if (o == null) return; + if (o is List ints && ints.Count == 2 && morkFs.ContainsKey(num)) { - MessageLog.GetInstance.Show("搅拌臂不在原点位,无法完成出餐"); - return; + FirePot_Write(morkFs[num].PassWayValue[ints[0]], (ushort)ints[1], num);//写入通道值 + Thread.Sleep(400); + FirePot_Write(morkFs[num].StartPassWay[ints[0]], true, num);//开启通道 + Thread.Sleep(400); + FirePot_Write(morkFs[num].StartPassWay[ints[0]], false, num);//开启通道 } - FirePot1_Write("LB4", true, num); - Thread.Sleep(200); - FirePot1_Write("LB4", false, num); } - //加热挡位设定 - public void SetFire(object o, int num) + + /// + /// 出多个调料 + /// + public void OutSeasonings(List seasoningLists, int num) { - if (o == null) return; - if (o is List ints && ints.Count == 1) + //防止越界 + if (!morkFs.ContainsKey(num)) { - FirePot1_Write("LW14", (ushort)ints[0],num); - Thread.Sleep(200); - + return; } - } + foreach (SeasoningList seasoning in seasoningLists) + { + FirePot_Write(morkFs[num].PassWayValue[seasoning.Loc], (ushort)seasoning.Qty, num); + Thread.Sleep(300); + } + foreach (SeasoningList seasoning in seasoningLists) + { + FirePot_Write(morkFs[num].StartPassWay[seasoning.Loc], true, num); + Thread.Sleep(300); + } + foreach (SeasoningList seasoning in seasoningLists) + { + FirePot_Write(morkFs[num].StartPassWay[seasoning.Loc], false, num); + Thread.Sleep(300); + } - public void SetFry(object o) - { - if (o == null) return; - if (o is List ints && ints.Count == 1) + foreach (SeasoningList seasoning in seasoningLists) { - fryIndex = ints[0] - 1; + FirePot_Write(morkFs[num].StartPassWay[seasoning.Loc], false, num); + Thread.Sleep(300); } } /// - /// 搅拌挡位设定 + /// 复位 /// - /// - public void SetStir(object o, int num) + public void Plc1Reset(int num) { - if (o == null) return; - if (o is List ints && ints.Count == 1) + ThreadManage.GetInstance().Start(new Action(() => { - FirePot1_Write("LW15", (ushort)ints[0],num); + StopFire(num); Thread.Sleep(200); - - } - } - - #endregion - + StopStir(num); + Thread.Sleep(200); + FirePot_Write("LB5", false, num); + Thread.Sleep(200); + FirePot_Write("LB3", false, num); + Thread.Sleep(200); + FirePot_Write("LB6", false, num); + Thread.Sleep(200); + FirePot_Write("LB7", false, num); + Thread.Sleep(200); + FirePot_Write("LB4", false, num); + Thread.Sleep(200); + FirePot_Write("LB53", false, num); + if (morkFs.ContainsKey(num)) + { + foreach (var item in morkFs[num].StartPassWay.Values) + { + Thread.Sleep(200); + FirePot_Write(item, false, num); + } + } + }), "炒锅1初始化"); + } - /// - /// 本地菜单下单 + /// 炒锅初始化 /// - private void StartOrder(object o) + public void PLCInite() { - if(o==null) return; - if(o is int goodId) + for (int i = 0; i < morkFs.Count; i++) { - var res = LocalstirFryGoods?.FirstOrDefault(p => p.GoodsKey == goodId);//匹配订单对应制作流程 - if (res != null) + FirePot_Write("LB0", true, i); + + for (int j = 0; j < sleepCount && !morkFs[i].FryPot1_InitialComplete; j++) { - /* morkF.listStirBom.Add(res.StirFryBomInfo);*///添加订单制作流程 - if (StirFryGoodsQuenes.Count > 0) return; - StirFryGoodsQuenes.Enqueue(new OrderLocInfo() + Thread.Sleep(sleepTime); + if (j >= sleepCount - 1) { - SuborderId = Guid.NewGuid().ToString(), - StirPotActions = res.StirPotActions, - GoodName = "本地菜品" - }); - MessageLog.GetInstance.Show($"添加本地订单{res.GoodsKey}"); + MessageLog.GetInstance.Show($"炒锅{j}初始化超时"); + } } + + //while (!morkFs[i].FryPot1_InitialComplete) + //{ + // Thread.Sleep(500); + //} + FirePot_Write("LB0", false, i); } } - private void StartLocalOrder() + //加油 + public void AddOil() { - if (StirFryGoodsQuenes.Count > 0) return;//只能一个一个做 - if (Json.Data.LocalstirFryGoods.StirPotActions.Count>0) + + } + //加热启动 + public void StartFire(int num) + { + FirePot_Write("LB1", true, num); + Thread.Sleep(200); + } + //加热停止 + public void StopFire(int num) + { + FirePot_Write("LB1", false, num); + Thread.Sleep(200); + } + //搅拌启动 + public void StartStir(int num) + { + FirePot_Write("LB2", true, num); + Thread.Sleep(200); + } + //搅拌启停止 + public void StopStir(int num) + { + FirePot_Write("LB2", false, num); + Thread.Sleep(200); + } + //倒菜 + public void OutFood(int num) + { + if (!morkFs.ContainsKey(num)) { - StirFryGoodsQuenes.Enqueue(new OrderLocInfo() + return; + } + + FirePot_Write("LB3", true, num); + MessageLog.GetInstance.Show("倒菜启动"); + + Thread.Sleep(400); + + for (int i = 0; i < sleepCount && !morkFs[num].FryPot1_MaterialIntoPot; i++) + { + Thread.Sleep(sleepTime); + if (i >= sleepCount - 1) { - SuborderId = Guid.NewGuid().ToString(), - StirPotActions = Json.Data.LocalstirFryGoods.StirPotActions, - GoodName = "本地菜品" - }); - MessageLog.GetInstance.Show($"添加本地模拟的订单{Json.Data.LocalstirFryGoods.GoodsKey}"); + MessageLog.GetInstance.Show($"炒锅{num}倒菜超时"); + } } + + FirePot_Write("LB3", false, num); + Thread.Sleep(200); + MessageLog.GetInstance.Show("倒菜完成"); } + //搅拌臂去原点位 + public void StirArmGoOrigin(int num) + { + if (!morkFs.ContainsKey(num)) + { + return; + } + + FirePot_Write("LB5", true, num); + MessageLog.GetInstance.Show("搅拌臂去原点位"); + + for (int i = 0; i < sleepCount && !morkFs[num].ArmOnOrigin; i++) + { + Thread.Sleep(sleepTime); + if (i >= sleepCount - 1) + { + MessageLog.GetInstance.Show($"炒锅{num}搅拌臂去原点位超时"); + } + } + + //while (!morkFs[num].ArmOnOrigin) + //{ + // Thread.Sleep(200); + //} + FirePot_Write("LB5", false, num); + Thread.Sleep(200); + MessageLog.GetInstance.Show("搅拌臂到达原点位"); + - private void StopLocalOrder() - { - //判断当前是否有炒制菜品 - if (nowStirFryGood == null) return; - ThreadManage.GetInstance().StopTask(String.Format(striConst, fryIndex.ToString(), nowStirFryGood.GoodName), new Action(() => { Plc1Reset(fryIndex); })); } - #endregion - public override void ResetProgram() + //搅拌臂去炒制位 + public void StirArmGoWork(int num) { - morkFs.Clear(); - morkFs = new Dictionary(); - - for (int i = 0; i < count; i++) + if (!morkFs.ContainsKey(num)) { - morkFs.Add(i, new GVL_MorkF()); + return; } - } - - /// - /// PLC数据读取 - /// - public override void ReadData() - { - for (int i = 0; i < morkFs.Count; i++) + if (!morkFs[num].ArmOnWorking/* && morkFs[num].PotOnOrigin*/) { - GetStatus("LB50", new Action((objects) => - { - if (!morkFs.ContainsKey(i)) - { - return; - } - if(objects is bool[] bools) - { - morkFs[i].FryPot1_InitialComplete = bools[0]; - morkFs[i].FryPot1_HOBTPut = bools[1]; - morkFs[i].FryPot1_HOBTGet = bools[2]; - morkFs[i].FryPot1_MaterialIntoPot = bools[3]; - morkFs[i].OutFoodCompelete = bools[4]; - morkFs[i].CanOutFood = bools[5]; - morkFs[i].GetFoodCompelete = bools[6]; - morkFs[i].CanOutPotWashingWater = bools[7]; - morkFs[i].ArmOnOrigin = bools[8]; - morkFs[i].ArmOnWorking = bools[9]; - morkFs[i].PotOnOrigin = bools[10]; - } + FirePot_Write("LB6", true, num); + MessageLog.GetInstance.Show("搅拌臂去工作位"); - }), i); - } - for (int j = 0; j < morkFs.Count; j++) - { - GetStatus("LB74", new Action((objects) => + for (int i = 0; i < sleepCount && !morkFs[num].ArmOnWorking; i++) { - if (!morkFs.ContainsKey(j)) + Thread.Sleep(sleepTime); + if (i >= sleepCount - 1) { - return; + MessageLog.GetInstance.Show($"炒锅{num}搅拌臂去炒制位超时"); } + } - if (objects is bool[] bools) - { - for (int i = 0; i < 14; i++) - { - morkFs[j].PassWay1_Compelete[i] = bools[i]; - } - } + //while (!morkFs[num].ArmOnWorking) + //{ + // Thread.Sleep(200); + //} - }), j); + FirePot_Write("LB6", false, num); + Thread.Sleep(200); + MessageLog.GetInstance.Show("搅拌臂到达工作位"); } - for (int i = 0; i < morkFs.Count; i++) - { - if (!morkFs.ContainsKey(i)) - { - return; - } - GetStatus("LB90", new Action((objects) => - { - if (objects is bool[] bools) - { - morkFs[i].AutoMode = bools[0]; - } + } + //HBOT放盒子到位 + public void HBOTGoWork(int num) + { + FirePot_Write("LB7", true, num); + Thread.Sleep(400); + FirePot_Write("LB7", false, num); + } + //出餐启动 + public void OutMeal(int num) + { + if (!morkFs[num].ArmOnOrigin /*&& morkFs[num].PotOnOrigin*/) + { + MessageLog.GetInstance.Show("搅拌臂不在原点位,无法完成出餐"); + return; + } + FirePot_Write("LB4", true, num); + Thread.Sleep(200); + FirePot_Write("LB4", false, num); + } + //加热挡位设定 + public void SetFire(object o, int num) + { + if (o == null) return; + if (o is List ints && ints.Count == 1) + { + FirePot_Write("LW14", (ushort)ints[0], num); + Thread.Sleep(200); - }), i); } + } - for (int j = 0; j < morkFs.Count; j++) + public void SetFry(object o) + { + if (o == null) return; + if (o is List ints && ints.Count == 1) { - GetStatus("VD808", new Action((objects) => - { - if (!morkFs.ContainsKey(j)) - { - return; - } - - if (objects is object[] bools) - { - for (int i = 0; i < 1; i++) - { - var a = bools[i]; - } - } - + fryIndex = ints[0] - 1; + } + } - }), j); - GetStatus("VD814", new Action((objects) => - { - if (!morkFs.ContainsKey(j)) - { - return; - } + /// + /// 搅拌挡位设定 + /// + /// + public void SetStir(object o, int num) + { + if (o == null) return; + if (o is List ints && ints.Count == 1) + { + FirePot_Write("LW15", (ushort)ints[0], num); + Thread.Sleep(200); - if (objects is object[] bools) - { - for (int i = 0; i < 1; i++) - { - var a = bools[i]; - } - } + } + } + #endregion - }), j); - GetStatus("VD816", new Action((objects) => - { - if (!morkFs.ContainsKey(j)) - { - return; - } - if (objects is object[] bools) - { - for (int i = 0; i < 1; i++) - { - var a = bools[i]; - } - } + bool isInitialArrive = false; - }), j); - } + bool isInitialPaw = false; - } + #region 联网交互 private void ServerInit() { //物料信息 @@ -773,15 +1212,23 @@ namespace BPASmartClient.MorkF if (@event == null) return; if (@event is StirFryGoodsEvent stirFry) { - if (stirFry.stirFrymessage.stirFryGoods.Count > 0) + if(stirFry.stirFrymessage != null) { - foreach (var item in stirFry.stirFrymessage.stirFryGoods) + if (stirFry.stirFrymessage.stirFryGoods.Count > 0) { - LocalstirFryGoods.Add(new StirFryGoods + foreach (var item in stirFry.stirFrymessage.stirFryGoods) { - GoodsKey = item.GoodsKey, - StirPotActions = OrderSort(item.StirPotActions), - }); + LocalstirFryGoods.Add(new StirFryGoods + { + GoodsKey = item.GoodsKey, + StirPotActions = OrderSort(item.StirPotActions), + }); + GlobalFoodMenu.LocalFoodMenus.Add(new FoodMenuModel + { + GoodKey = item.GoodsKey.ToString(), + GoodName = "", + }); + } } } @@ -887,7 +1334,11 @@ namespace BPASmartClient.MorkF }); } - + /// + /// 订单排序 + /// + /// + /// private List OrderSort(List potActions) { if (potActions.Count > 1) @@ -905,6 +1356,7 @@ namespace BPASmartClient.MorkF } return potActions; } + /// /// 订单状态发布 /// @@ -915,17 +1367,15 @@ namespace BPASmartClient.MorkF EventBus.EventBus.GetInstance().Publish(new OrderStatusChangedEvent() { Status = oRDER_STATUS, SubOrderId = subid }); } - - /// /// 数据解析 /// - private void DataParse(int num) + private void DataParse() { - if (!morkFs.ContainsKey(num)) - { - return; - } + //if (!morkFs.ContainsKey(num)) + //{ + // return; + //} EventBus.EventBus.GetInstance().Subscribe(DeviceId, delegate (IEvent @event, EventCallBackHandle callBackHandle) { @@ -937,40 +1387,47 @@ namespace BPASmartClient.MorkF DeviceProcessLogShow($"接收到{OrderCount}次订单"); Enum.GetNames(typeof(StirFryPotActionEnum)); - var res = LocalstirFryGoods?.FirstOrDefault(p => p.GoodsKey == order.MorkOrder.RecipeId);//匹配订单对应制作流程 - if (res != null) + //var res = LocalstirFryGoods?.FirstOrDefault(p => p.GoodsKey == order.MorkOrder.RecipeId);//匹配订单对应制作流程 + if(order.MorkOrder.GoodBatchings.Count <= 0) { - /* morkF.listStirBom.Add(res.StirFryBomInfo);*///添加订单制作流程 - //添加到带炒小炒队列 - if (StirFryGoodsQuenes.FirstOrDefault(p => p.SuborderId == order.MorkOrder.SuborderId) == null) - { - lock (lock_MainProcessExcute) - { - StirFryGoodsQuenes.Enqueue(new OrderLocInfo() - { - SuborderId = order.MorkOrder.SuborderId, - StirPotActions = res.StirPotActions, - GoodName = order.MorkOrder.GoodsName - }); - } - } + return; + } + + if(order.MorkOrder.DeviceId != DeviceId) + { + return; } + + //var res = LocalstirFryGoods[0]; + //if (res != null) + //{ + // /* morkF.listStirBom.Add(res.StirFryBomInfo);*///添加订单制作流程 + // //添加到带炒小炒队列 + // if (StirFryGoodsQuenes.FirstOrDefault(p => p.SuborderId == order.MorkOrder.SuborderId) == null) + // { + // lock (lock_MainProcessExcute) + // { + // StirFryGoodsQuenes.Enqueue(new OrderLocInfo() + // { + // SuborderId = order.MorkOrder.SuborderId, + // StirPotActions = res.StirPotActions, + // GoodName = order.MorkOrder.GoodsName + // }); + // } + // } + //} + + //暂时使用本地菜单 + StartLocalOrder(); } }); } - - /// - /// 主任务 - /// - public override void MainTask() - { - MainProcessExcute(); - //MinorProcessExcute(); - //SingleProcess(); - } + #endregion + private object lock_MainProcessExcute = new object(); + /// /// 炒锅主流程 /// @@ -982,7 +1439,7 @@ namespace BPASmartClient.MorkF //遍历炒锅,找到合适、空闲的炒锅 for(int i = 0;i= sleepCount - 1) - { - MessageLog.GetInstance.Show($"炒锅{i}炒制过程完成后,搅拌臂去原点位超时"); - } - } - //while (!morkFs[i].ArmOnOrigin) //{ // Thread.Sleep(100); @@ -1158,79 +1609,6 @@ namespace BPASmartClient.MorkF } } } - //辅流程执行 - //private void MinorProcessExcute() - //{ - // if (!morkF.MinorProcessExcuteLock) - // { - // morkF.MinorProcessExcuteLock = true; - // Task.Run(() => - // { - // if (morkF.MinorProcessFlag && morkF.TakeMaterialQueue.Count > 0 && morkF.listStirBom.Count > 0 ) - // { - // morkF.MinorProessStatus = true; - // morkF.MainProcessStatus = false; - // StirFryBom bom = morkF.listStirBom.ElementAt(0); - // morkF.listStirBom.RemoveAt(0); - // foreach (var res in bom.StirFryActions) - // { - // DeviceProcessLogShow($"执行流程{res.Time}"); - // //机器人线程 - // Task taskRobot = Task.Run(new Action(() => - // { - // foreach (var temp in res.RobotActions) - // { - // switch (temp) - // { - - // } - - // } - - // })); - // //炒锅线程操作 - // Task taskPot = Task.Run(new Action(() => - // { - // foreach (var temp in res.PotActions) - // { - // switch (temp) - // { - // case StirFryPotAction.NONE: - // break; - - // } - // } - // })); - // Task.WhenAll(taskRobot, taskPot).Wait();//等待所有线程结束 - // Task.Delay(res.During * 1000).Wait();//当前流程延迟 - // if (morkF.MainProcessWait) - // { - // if (morkF.MainHasTakeMaterial) - // { - - // } - // else - // { - // //关闭灶加热 - // //阻塞辅流程 - // minorReset.WaitOne(); - // //morkF.MinorProessStatus = true; - // //morkF.MainProcessStatus = false; - // } - - // } - - // } - // morkF.MinorOutMealComplete = true; - // } - // else - // { - // morkF.MinorProcessExcuteLock = false;//解除辅流程自锁 - // } - // }); - // } - //} - /// /// 信号处理 @@ -1250,67 +1628,6 @@ namespace BPASmartClient.MorkF - } - - /// - /// 获取炒锅PLC的所有状态 - /// - /// - /// - /// 炒锅编号 - private void GetStatus(string key, Action action,int num) - { - if(dicPort2peripheralStatus.ContainsKey(num)) - { - if (dicPort2peripheralStatus[num].ContainsKey(key)) - { - action((object)dicPort2peripheralStatus[num][key]);//获取PLC指定地址的状态值 - } - } - } - ///// - ///// 写数据 - ///// - ///// - ///// - //private void WriteData(string address, object value) - //{ - // EventBus.EventBus.GetInstance().Publish(new WriteModel() { DeviceId = DeviceId, Address = address, Value = value }); - //} - - /// - /// 炒锅初始化 - /// - public void PLCInite() - { - for(int i = 0;i=sleepCount-1) - { - MessageLog.GetInstance.Show($"炒锅{j}初始化超时"); - } - } - - //while (!morkFs[i].FryPot1_InitialComplete) - //{ - // Thread.Sleep(500); - //} - FirePot1_Write("LB0", false, i); - } - } - - - - /// - - public override void Stop() - { - } public override void SimOrder() diff --git a/BPASmartClient.MorkF/GVL_MorkF.cs b/BPASmartClient.MorkF/GVL_MorkF.cs index b5a16de2..47999617 100644 --- a/BPASmartClient.MorkF/GVL_MorkF.cs +++ b/BPASmartClient.MorkF/GVL_MorkF.cs @@ -1,4 +1,5 @@ using BPA.Models; +using BPASmartClient.Device; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -25,6 +26,7 @@ namespace BPASmartClient.MorkF /// /// 炒锅自动模式 /// + [VariableMonitor("炒锅1锅低温度", "VW120", "160")] public bool AutoMode { get; set; } /// /// 炒锅1初始化完成 diff --git a/BPASmartClient.MorkF/ML_MorkF.cs b/BPASmartClient.MorkF/ML_MorkF.cs new file mode 100644 index 00000000..74a66a0a --- /dev/null +++ b/BPASmartClient.MorkF/ML_MorkF.cs @@ -0,0 +1,127 @@ +using BPA.Helper; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BPASmartClient.MorkF +{ + internal class ML_MorkF + { + #region 菜品库的操作状态 + /// + /// 激光距离 + /// + public int LaserDistance { get; set; } + + /// + /// 菜品库当前X轴坐标 + /// + public int ArmPositionX { get; set; } + + /// + /// 菜品库当前Y轴坐标 + /// + public int ArmPositionY { get; set; } + + /// + /// 菜品库是否在工作中 + /// + public bool MaterailIsWorking { get; set; } + + /// + /// 初始化是否完成 + /// + public bool InitialComplete { get; set; } + + private bool _ArriveComplete = false; + /// + /// 定位到达,上升沿捕获需要特殊处理 + /// + public bool ArriveComplete + { + get + { + var ret = _ArriveComplete; + _ArriveComplete = false; + if(ret) + { + MessageLog.GetInstance.Show("到达上升沿为true"); + } + else + { + MessageLog.GetInstance.Show("到达上升沿为false"); + } + return ret; + } + set + { + _ArriveComplete = value; + } + } + + /// + /// 爪子初始化完成 + /// + public bool PawInitialComplete { get; set; } + + private bool _PawArrivePortOne = false; + /// + /// 爪子到达一号位,上升沿捕获需要特殊处理 + /// + public bool PawArrivePortOne + { + get + { + var ret = _PawArrivePortOne; + _PawArrivePortOne = false; + return ret; + } + set + { + _PawArrivePortOne = value; + } + } + + private bool _PawArrivePortTwo = false; + /// + /// 爪子到达二号位,上升沿捕获需要特殊处理 + /// + public bool PawArrivePortTwo + { + get + { + var ret = _PawArrivePortTwo; + _PawArrivePortTwo = false; + return ret; + } + set + { + _PawArrivePortTwo = value; + } + } + + private bool _PawArrivePortThree = false; + /// + /// 爪子到达三号位 + /// + public bool PawArrivePortThree + { + get + { + var ret = _PawArrivePortThree; + _PawArrivePortThree = false; + return ret; + } + set + { + _PawArrivePortThree = value; + } + } + #endregion + + #region + #endregion + } +} diff --git a/BPASmartClient.MorkF/View/DebugView.xaml b/BPASmartClient.MorkF/View/DebugView.xaml index 26a99eca..63e9fecd 100644 --- a/BPASmartClient.MorkF/View/DebugView.xaml +++ b/BPASmartClient.MorkF/View/DebugView.xaml @@ -97,9 +97,9 @@