diff --git a/Common/09MesDataFunction/MesDataFunction/13MIS_FuntionSaveData/Funtion.MesRead.cs b/Common/09MesDataFunction/MesDataFunction/13MIS_FuntionSaveData/Funtion.MesRead.cs
index 3866226..2acbf0e 100644
--- a/Common/09MesDataFunction/MesDataFunction/13MIS_FuntionSaveData/Funtion.MesRead.cs
+++ b/Common/09MesDataFunction/MesDataFunction/13MIS_FuntionSaveData/Funtion.MesRead.cs
@@ -158,8 +158,6 @@ namespace MisDataSaveDateMesRead
//保存质量数
BaseData_tagTable_QualityData_B5A_IndexInsert(engineID, tagID, engineType, engineTypeID, opName, qualityMask, partPallet, out id_tagCF);
BaseData_tagTable_QualityData_B5A_Insert(id_tagCF, engineTypeID, engineID, opName, dt_WWW, isCheck);
- //保存质量数据到 接口表
- MisDataFun.SaveQualityInterfaceData(opName, engineID);
}
catch (Exception err)
{
diff --git a/Common/09MesDataFunction/MesDataFunction/14MisDataFunctionDB/MisDataFun.DB.cs b/Common/09MesDataFunction/MesDataFunction/14MisDataFunctionDB/MisDataFun.DB.cs
index 6047738..5810bc1 100644
--- a/Common/09MesDataFunction/MesDataFunction/14MisDataFunctionDB/MisDataFun.DB.cs
+++ b/Common/09MesDataFunction/MesDataFunction/14MisDataFunctionDB/MisDataFun.DB.cs
@@ -212,13 +212,14 @@ namespace MisDataFunDll
///
///
///
- public static DataTable IOT_ReqData_UpLoad_IsSend(string opName, string OrderFrom, string reqType)
+ public static DataTable IOT_ReqData_UpLoad_IsSend(string opName, string OrderFrom, string reqType, string engineID = "")
{
string produceName = "接口_IOT接口_报工记录_是否报工判断";
- SqlParameter[] thisParms = new SqlParameter[3];
+ SqlParameter[] thisParms = new SqlParameter[4];
thisParms[0] = new System.Data.SqlClient.SqlParameter("@工位号", opName);
thisParms[1] = new System.Data.SqlClient.SqlParameter("@订单号", OrderFrom);
thisParms[2] = new System.Data.SqlClient.SqlParameter("@报工类型", reqType);
+ thisParms[3] = new System.Data.SqlClient.SqlParameter("@工件编号", engineID ?? "");
DataAccess.ExecuteStoredProcedure(produceName, ref thisParms, out DataTable dt);
return dt;
}
@@ -1772,6 +1773,19 @@ namespace MisDataFunDll
return dt;
}
+ ///
+ /// 根据工件编号查询派工订单执行计划信息
+ ///
+ public static DataTable XD_GetPlanInfoByEngineID(string engineID)
+ {
+ DataTable dt = new DataTable();
+ string procedureName = "XD_派工订单执行计划_按工件编号查询";
+ SqlParameter[] thisParms = new SqlParameter[1];
+ thisParms[0] = new System.Data.SqlClient.SqlParameter("@工件编号", engineID ?? "");
+ DataAccess.ExecuteStoredProcedure(procedureName, ref thisParms, out dt);
+ return dt;
+ }
+
///
/// 报警工位记录表_MOBY_查询
///
diff --git a/DatabaseScripts/AGV举升回调_现场总装.sql b/DatabaseScripts/AGV举升回调_现场总装.sql
deleted file mode 100644
index 0a24d11..0000000
--- a/DatabaseScripts/AGV举升回调_现场总装.sql
+++ /dev/null
@@ -1,95 +0,0 @@
--- 现场总装数据库:MESBasicDB_XD_ZZ
--- 用途:支持 AGV lower/raise 回调与 PLC 允许下降/举升放行查询。
--- 说明:请在目标库手工执行。本脚本不直接修改 AGV_光栅请求记录 表结构。
-
-SET NOCOUNT ON;
-GO
-
--- 1. 增加变量类型代码。500/501 是 MES 写 PLC 请求位,550/551 是 PLC 允许位。
-IF NOT EXISTS (SELECT 1 FROM [dbo].[MES_基本变量变量类型代码] WHERE [变量类型代码] = 500)
-BEGIN
- INSERT INTO [dbo].[MES_基本变量变量类型代码]
- ([变量类型], [TagTypeDataType], [TagName], [变量类型代码], [TagTypeChange], [MesTagTypeChange], [WebTagTypeChange], [信号点名称], [变量类型名称])
- VALUES
- (N'X', 11, N'AGV请求下降', 500, 1, 0, 1, N'AGV请求下降', N'AGV请求下降');
-END
-
-IF NOT EXISTS (SELECT 1 FROM [dbo].[MES_基本变量变量类型代码] WHERE [变量类型代码] = 501)
-BEGIN
- INSERT INTO [dbo].[MES_基本变量变量类型代码]
- ([变量类型], [TagTypeDataType], [TagName], [变量类型代码], [TagTypeChange], [MesTagTypeChange], [WebTagTypeChange], [信号点名称], [变量类型名称])
- VALUES
- (N'X', 11, N'AGV请求举升', 501, 1, 0, 1, N'AGV请求举升', N'AGV请求举升');
-END
-
-IF NOT EXISTS (SELECT 1 FROM [dbo].[MES_基本变量变量类型代码] WHERE [变量类型代码] = 550)
-BEGIN
- INSERT INTO [dbo].[MES_基本变量变量类型代码]
- ([变量类型], [TagTypeDataType], [TagName], [变量类型代码], [TagTypeChange], [MesTagTypeChange], [WebTagTypeChange], [信号点名称], [变量类型名称])
- VALUES
- (N'X', 11, N'允许AGV下降', 550, 1, 0, 1, N'允许AGV下降', N'允许AGV下降');
-END
-
-IF NOT EXISTS (SELECT 1 FROM [dbo].[MES_基本变量变量类型代码] WHERE [变量类型代码] = 551)
-BEGIN
- INSERT INTO [dbo].[MES_基本变量变量类型代码]
- ([变量类型], [TagTypeDataType], [TagName], [变量类型代码], [TagTypeChange], [MesTagTypeChange], [WebTagTypeChange], [信号点名称], [变量类型名称])
- VALUES
- (N'X', 11, N'允许AGV举升', 551, 1, 0, 1, N'允许AGV举升', N'允许AGV举升');
-END
-GO
-
--- 2. 扩展 AGV continueTask 放行查询。
--- apply/release 保持原有 XD_库位Moby 查询。
--- lower/raise 从 AGV_光栅请求记录 取最新未允许或最近请求,避免改动 XD_库位Moby 与历史表列顺序。
-ALTER PROCEDURE [dbo].[AGV_允许进入离开_查询]
- @opName nvarchar(50) = 'AC0901-02',
- @engineID nvarchar(50) = '0',
- @type nvarchar(50) = 'apply'
-AS
-BEGIN
- SET NOCOUNT ON;
-
- IF (@type = 'apply')
- BEGIN
- SELECT [进入req] AS reqCode, [进入task] AS taskCode
- FROM [dbo].[XD_库位Moby]
- WHERE [库位号] = @opName
- AND [AGV请求进入时间] IS NOT NULL;
- END
-
- IF (@type = 'release')
- BEGIN
- SELECT [离开req] AS reqCode, [离开task] AS taskCode
- FROM [dbo].[XD_库位Moby]
- WHERE [库位号] = @opName
- AND [AGV请求离开时间] IS NOT NULL;
- END
-
- IF (@type = 'lower')
- BEGIN
- SELECT TOP 1 [reqCode], [taskCode]
- FROM [dbo].[AGV_光栅请求记录]
- WHERE [method] = 'lower'
- AND (
- [currentPositionCode] = @opName
- OR [currentPositionCode] LIKE @opName + N'[_]%'
- OR [wbCode] = @opName
- )
- ORDER BY ISNULL([是否允许], 0) ASC, [创建时间] DESC, [ID] DESC;
- END
-
- IF (@type = 'raise')
- BEGIN
- SELECT TOP 1 [reqCode], [taskCode]
- FROM [dbo].[AGV_光栅请求记录]
- WHERE [method] = 'raise'
- AND (
- [currentPositionCode] = @opName
- OR [currentPositionCode] LIKE @opName + N'[_]%'
- OR [wbCode] = @opName
- )
- ORDER BY ISNULL([是否允许], 0) ASC, [创建时间] DESC, [ID] DESC;
- END
-END
-GO
diff --git a/DatabaseScripts/TCS_AGV接口仿真服务.mjs b/DatabaseScripts/TCS_AGV接口仿真服务.mjs
new file mode 100644
index 0000000..799e096
--- /dev/null
+++ b/DatabaseScripts/TCS_AGV接口仿真服务.mjs
@@ -0,0 +1,115 @@
+import http from 'node:http'
+
+const host = '127.0.0.1'
+const port = Number(process.env.TCS_MOCK_PORT || 18080)
+const tasks = new Map()
+
+const sendJson = (response, payload, statusCode = 200) => {
+ response.writeHead(statusCode, { 'Content-Type': 'application/json; charset=utf-8' })
+ response.end(JSON.stringify(payload))
+}
+
+const success = result => ({
+ success: true,
+ message: '仿真接口调用成功',
+ code: 200,
+ result,
+ timestamp: Date.now()
+})
+
+const readBody = request => new Promise((resolve, reject) => {
+ let body = ''
+ request.setEncoding('utf8')
+ request.on('data', chunk => { body += chunk })
+ request.on('end', () => {
+ if (!body) {
+ resolve({})
+ return
+ }
+ try {
+ resolve(JSON.parse(body))
+ } catch (error) {
+ reject(error)
+ }
+ })
+ request.on('error', reject)
+})
+
+const server = http.createServer(async (request, response) => {
+ const url = new URL(request.url, `http://${host}:${port}`)
+ try {
+ if (request.method === 'POST' && url.pathname === '/api/createExternalBusinessTask') {
+ const body = await readBody(request)
+ tasks.set(body.externalCode, body)
+ console.log('CREATE', JSON.stringify(body))
+ sendJson(response, success({ externalCode: body.externalCode }))
+ return
+ }
+
+ if (request.method === 'GET' && url.pathname === '/api/getExternalBusinessTaskStatus') {
+ const externalCode = url.searchParams.get('externalCode') || ''
+ sendJson(response, success([{
+ externalCode,
+ status: '1',
+ ip: '10.146.79.9',
+ type: tasks.get(externalCode)?.type || 'nav_action_program',
+ beginTime: '2026-07-13 14:40:00',
+ endTime: '',
+ remark: '仿真状态同步'
+ }]))
+ return
+ }
+
+ if (request.method === 'GET' && url.pathname === '/api/getMcrInfo') {
+ sendJson(response, success([{
+ name: 'AGV-01',
+ ip: '10.146.79.9',
+ speed: 0,
+ power: 80,
+ autoCharging: '1',
+ chargingThreshold: 20,
+ status: '0',
+ currentPos: 'S000214',
+ control: '1',
+ CameraInfo: ''
+ }]))
+ return
+ }
+
+ const successfulGetRoutes = new Set([
+ '/api/cancelExternalBusinessTask',
+ '/api/stopExternalBusinessTask',
+ '/api/reset',
+ '/api/stopNow'
+ ])
+ if (request.method === 'GET' && successfulGetRoutes.has(url.pathname)) {
+ console.log('GET', url.pathname, url.search)
+ sendJson(response, success('成功'))
+ return
+ }
+
+ sendJson(response, {
+ success: false,
+ message: `未实现的仿真路由:${request.method} ${url.pathname}`,
+ code: 500,
+ result: null,
+ timestamp: Date.now()
+ }, 404)
+ } catch (error) {
+ sendJson(response, {
+ success: false,
+ message: error.message,
+ code: 500,
+ result: null,
+ timestamp: Date.now()
+ }, 500)
+ }
+})
+
+server.listen(port, host, () => {
+ console.log(`TCS AGV mock listening on http://${host}:${port}`)
+})
+
+const close = () => server.close(() => process.exit(0))
+process.on('SIGINT', close)
+process.on('SIGTERM', close)
diff --git a/DatabaseScripts/XD_TCS_AGV插拔任务.sql b/DatabaseScripts/XD_TCS_AGV插拔任务.sql
new file mode 100644
index 0000000..59bfcea
--- /dev/null
+++ b/DatabaseScripts/XD_TCS_AGV插拔任务.sql
@@ -0,0 +1,711 @@
+SET NOCOUNT ON
+GO
+
+IF OBJECT_ID(N'dbo.XD_TCS_AGV_配置', N'U') IS NULL
+BEGIN
+ CREATE TABLE dbo.XD_TCS_AGV_配置
+ (
+ 配置键 nvarchar(50) NOT NULL CONSTRAINT PK_XD_TCS_AGV_配置 PRIMARY KEY,
+ 配置值 nvarchar(500) NOT NULL,
+ 配置说明 nvarchar(200) NULL,
+ 是否启用 bit NOT NULL CONSTRAINT DF_XD_TCS_AGV_配置_是否启用 DEFAULT(1),
+ 操作时间 datetime NOT NULL CONSTRAINT DF_XD_TCS_AGV_配置_操作时间 DEFAULT(GETDATE())
+ )
+END
+GO
+
+IF OBJECT_ID(N'dbo.XD_TCS_AGV_点位配置', N'U') IS NULL
+BEGIN
+ CREATE TABLE dbo.XD_TCS_AGV_点位配置
+ (
+ ID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_XD_TCS_AGV_点位配置 PRIMARY KEY,
+ 产线代码 nvarchar(20) NOT NULL,
+ MES工位号 nvarchar(50) NOT NULL,
+ 现场位置 nvarchar(50) NOT NULL,
+ WorkStation nvarchar(50) NOT NULL,
+ 插XSDProgramName nvarchar(100) NOT NULL,
+ 插XSEProgramName nvarchar(100) NOT NULL,
+ 拔XSDProgramName nvarchar(100) NOT NULL,
+ 拔XSEProgramName nvarchar(100) NOT NULL,
+ 排序 int NOT NULL,
+ 是否启用 bit NOT NULL CONSTRAINT DF_XD_TCS_AGV_点位配置_是否启用 DEFAULT(1),
+ 操作时间 datetime NOT NULL CONSTRAINT DF_XD_TCS_AGV_点位配置_操作时间 DEFAULT(GETDATE()),
+ CONSTRAINT UK_XD_TCS_AGV_点位配置_MES工位号 UNIQUE(MES工位号),
+ CONSTRAINT UK_XD_TCS_AGV_点位配置_WorkStation UNIQUE(WorkStation)
+ )
+END
+GO
+
+IF OBJECT_ID(N'dbo.XD_TCS_AGV_型号动作映射', N'U') IS NULL
+BEGIN
+ CREATE TABLE dbo.XD_TCS_AGV_型号动作映射
+ (
+ ID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_XD_TCS_AGV_型号动作映射 PRIMARY KEY,
+ 产线代码 nvarchar(20) NOT NULL,
+ 产品型号 nvarchar(100) NOT NULL,
+ 任务动作 nvarchar(10) NOT NULL,
+ 执行XSD bit NOT NULL,
+ 执行XSE bit NOT NULL,
+ 是否启用 bit NOT NULL CONSTRAINT DF_XD_TCS_AGV_型号动作映射_是否启用 DEFAULT(1),
+ 操作时间 datetime NOT NULL CONSTRAINT DF_XD_TCS_AGV_型号动作映射_操作时间 DEFAULT(GETDATE()),
+ CONSTRAINT UK_XD_TCS_AGV_型号动作映射 UNIQUE(产线代码,产品型号,任务动作),
+ CONSTRAINT CK_XD_TCS_AGV_型号动作映射_动作 CHECK(任务动作 IN(N'插',N'拔')),
+ CONSTRAINT CK_XD_TCS_AGV_型号动作映射_组合 CHECK(执行XSD=1 OR 执行XSE=1)
+ )
+END
+GO
+
+IF OBJECT_ID(N'dbo.XD_TCS_AGV_任务', N'U') IS NULL
+BEGIN
+ CREATE TABLE dbo.XD_TCS_AGV_任务
+ (
+ ID bigint IDENTITY(1,1) NOT NULL CONSTRAINT PK_XD_TCS_AGV_任务 PRIMARY KEY,
+ 外部任务编号 nvarchar(100) NOT NULL,
+ 原任务编号 nvarchar(100) NULL,
+ MES工位号 nvarchar(50) NOT NULL,
+ 现场位置 nvarchar(50) NULL,
+ TCS站点 nvarchar(50) NULL,
+ 任务动作 nvarchar(20) NOT NULL,
+ 产品型号 nvarchar(100) NULL,
+ 动作组合 nvarchar(20) NULL,
+ ProgramName nvarchar(100) NULL,
+ 任务类型 nvarchar(50) NOT NULL,
+ 参数 nvarchar(max) NOT NULL,
+ 优先级 int NOT NULL,
+ 机器人组 nvarchar(50) NULL,
+ 机器人IP nvarchar(50) NULL,
+ 来源 nvarchar(20) NOT NULL,
+ 操作人 nvarchar(50) NULL,
+ 任务状态 int NOT NULL CONSTRAINT DF_XD_TCS_AGV_任务_任务状态 DEFAULT(0),
+ 状态说明 nvarchar(50) NOT NULL CONSTRAINT DF_XD_TCS_AGV_任务_状态说明 DEFAULT(N'未开始'),
+ 开始时间 nvarchar(50) NULL,
+ 结束时间 nvarchar(50) NULL,
+ TCS备注 nvarchar(1000) NULL,
+ 请求报文 nvarchar(max) NULL,
+ 响应报文 nvarchar(max) NULL,
+ 错误信息 nvarchar(2000) NULL,
+ 是否通知PLC bit NOT NULL CONSTRAINT DF_XD_TCS_AGV_任务_是否通知PLC DEFAULT(0),
+ PLC已通知 bit NOT NULL CONSTRAINT DF_XD_TCS_AGV_任务_PLC已通知 DEFAULT(0),
+ 处理状态 int NOT NULL CONSTRAINT DF_XD_TCS_AGV_任务_处理状态 DEFAULT(0),
+ 已复位 bit NOT NULL CONSTRAINT DF_XD_TCS_AGV_任务_已复位 DEFAULT(0),
+ 处理人 nvarchar(50) NULL,
+ 处理时间 datetime NULL,
+ 处理说明 nvarchar(1000) NULL,
+ 创建时间 datetime NOT NULL CONSTRAINT DF_XD_TCS_AGV_任务_创建时间 DEFAULT(GETDATE()),
+ 更新时间 datetime NOT NULL CONSTRAINT DF_XD_TCS_AGV_任务_更新时间 DEFAULT(GETDATE()),
+ CONSTRAINT UK_XD_TCS_AGV_任务_外部任务编号 UNIQUE(外部任务编号),
+ CONSTRAINT CK_XD_TCS_AGV_任务_状态 CHECK(任务状态 IN (0,1,2,3,4,5))
+ )
+
+ CREATE INDEX IX_XD_TCS_AGV_任务_状态时间
+ ON dbo.XD_TCS_AGV_任务(任务状态, 创建时间 DESC)
+ CREATE INDEX IX_XD_TCS_AGV_任务_工位时间
+ ON dbo.XD_TCS_AGV_任务(MES工位号, 创建时间 DESC)
+END
+GO
+
+IF COL_LENGTH(N'dbo.XD_TCS_AGV_任务', N'产品型号') IS NULL
+ ALTER TABLE dbo.XD_TCS_AGV_任务 ADD 产品型号 nvarchar(100) NULL
+GO
+
+IF COL_LENGTH(N'dbo.XD_TCS_AGV_任务', N'动作组合') IS NULL
+ ALTER TABLE dbo.XD_TCS_AGV_任务 ADD 动作组合 nvarchar(20) NULL
+GO
+
+IF COL_LENGTH(N'dbo.XD_TCS_AGV_任务', N'已复位') IS NULL
+BEGIN
+ ALTER TABLE dbo.XD_TCS_AGV_任务 ADD 已复位 bit NOT NULL
+ CONSTRAINT DF_XD_TCS_AGV_任务_已复位 DEFAULT(0)
+END
+GO
+
+IF EXISTS
+(
+ SELECT 1 FROM sys.check_constraints
+ WHERE parent_object_id=OBJECT_ID(N'dbo.XD_TCS_AGV_任务')
+ AND name=N'CK_XD_TCS_AGV_任务_状态'
+)
+ ALTER TABLE dbo.XD_TCS_AGV_任务 DROP CONSTRAINT CK_XD_TCS_AGV_任务_状态
+GO
+
+ALTER TABLE dbo.XD_TCS_AGV_任务 ADD CONSTRAINT CK_XD_TCS_AGV_任务_状态
+ CHECK(任务状态 IN (0,1,2,3,4,5))
+GO
+
+IF OBJECT_ID(N'dbo.XD_TCS_AGV_位置状态', N'U') IS NULL
+BEGIN
+ CREATE TABLE dbo.XD_TCS_AGV_位置状态
+ (
+ ID int NOT NULL CONSTRAINT PK_XD_TCS_AGV_位置状态 PRIMARY KEY,
+ 机器人IP nvarchar(50) NULL,
+ 当前站点 nvarchar(100) NULL,
+ 目标站点 nvarchar(100) NULL,
+ 原始报文 nvarchar(max) NULL,
+ 更新时间 datetime NOT NULL CONSTRAINT DF_XD_TCS_AGV_位置状态_更新时间 DEFAULT(GETDATE()),
+ CONSTRAINT CK_XD_TCS_AGV_位置状态_单车 CHECK(ID = 1)
+ )
+END
+GO
+
+IF OBJECT_ID(N'dbo.XD_TCS_AGV_操作记录', N'U') IS NULL
+BEGIN
+ CREATE TABLE dbo.XD_TCS_AGV_操作记录
+ (
+ ID bigint IDENTITY(1,1) NOT NULL CONSTRAINT PK_XD_TCS_AGV_操作记录 PRIMARY KEY,
+ 外部任务编号 nvarchar(100) NULL,
+ MES工位号 nvarchar(50) NULL,
+ 操作类型 nvarchar(50) NOT NULL,
+ 操作人 nvarchar(50) NULL,
+ 请求报文 nvarchar(max) NULL,
+ 响应报文 nvarchar(max) NULL,
+ 是否成功 bit NOT NULL,
+ 操作结果 nvarchar(1000) NULL,
+ 操作时间 datetime NOT NULL CONSTRAINT DF_XD_TCS_AGV_操作记录_操作时间 DEFAULT(GETDATE())
+ )
+
+ CREATE INDEX IX_XD_TCS_AGV_操作记录_时间
+ ON dbo.XD_TCS_AGV_操作记录(操作时间 DESC)
+END
+GO
+
+DECLARE @默认配置 TABLE
+(
+ 配置键 nvarchar(50),
+ 配置值 nvarchar(500),
+ 配置说明 nvarchar(200)
+)
+
+INSERT INTO @默认配置 VALUES
+(N'TCS_BaseUrl', N'http://10.112.111.21:11008', N'TCS调度服务地址,客户已确认'),
+(N'TCS_Enabled', N'0', N'1启用真实下发,0仅允许查询配置'),
+(N'McrGroup', N'G001', N'机器人组'),
+(N'McrIp', N'10.146.79.9', N'客户确认的单台AGV IP'),
+(N'TCS_OperatorUserId', N'BZS001', N'TCS任务状态定向推送标识'),
+(N'DefaultPriority', N'1', N'默认任务优先级'),
+(N'HttpTimeoutSeconds', N'15', N'TCS HTTP超时秒数')
+
+MERGE dbo.XD_TCS_AGV_配置 AS T
+USING @默认配置 AS S ON T.配置键 = S.配置键
+WHEN NOT MATCHED THEN
+ INSERT(配置键,配置值,配置说明) VALUES(S.配置键,S.配置值,S.配置说明);
+GO
+
+DECLARE @产线代码 nvarchar(20)
+SELECT TOP 1 @产线代码 = 值 FROM dbo.基本配置 WHERE 参数名称 = N'产线名称'
+
+DECLARE @点位 TABLE
+(
+ 产线代码 nvarchar(20), MES工位号 nvarchar(50), 现场位置 nvarchar(50), WorkStation nvarchar(50),
+ 插XSD nvarchar(100), 插XSE nvarchar(100), 拔XSD nvarchar(100), 拔XSE nvarchar(100), 排序 int
+)
+
+IF @产线代码 = N'GLKG'
+BEGIN
+ INSERT INTO @点位 VALUES
+ (N'GLKG',N'AC0909-01',N'GIS1',N'S000214',N'214GIS1插XSD',N'214GIS1插XSE',N'214GIS1拔XSD',N'214GIS1拔XSE',1),
+ (N'GLKG',N'AC0909-02',N'GIS2',N'S000215',N'215GIS2插XSD',N'215GIS2插XSE',N'215GIS2拔XSD',N'215GIS2拔XSE',2),
+ (N'GLKG',N'AC0909-03',N'GIS3',N'S000217',N'217GIS3插XSD',N'217GIS3插XSE',N'217GIS3拔XSD',N'217GIS3拔XSE',3),
+ (N'GLKG',N'AC0909-04',N'GIS4',N'S000218',N'218GIS4插XSD',N'218GIS4插XSE',N'218GIS4拔XSD',N'218GIS4拔XSE',4),
+ (N'GLKG',N'AC0909-05',N'GIS5',N'S000219',N'219GIS5插XSD',N'219GIS5插XSE',N'219GIS5拔XSD',N'219GIS5拔XSE',5),
+ (N'GLKG',N'AC0909-06',N'GIS6',N'S000220',N'220GIS6插XSD',N'220GIS6插XSE',N'220GIS6拔XSD',N'220GIS6拔XSE',6),
+ (N'GLKG',N'AC0909-07',N'GIS7',N'S000221',N'221GIS7插XSD',N'221GIS7插XSE',N'221GIS7拔XSD',N'221GIS7拔XSE',7),
+ (N'GLKG',N'AC0909-08',N'GIS8',N'S000222',N'222GIS8插XSD',N'222GIS8插XSE',N'222GIS8拔XSD',N'222GIS8拔XSE',8),
+ (N'GLKG',N'AC0909-09',N'GIS9',N'S000201',N'201GIS9插XSD',N'201GIS9插XSE',N'201GIS9拔XSD',N'201GIS9拔XSE',9),
+ (N'GLKG',N'AC0909-10',N'GIS10',N'S000202',N'202GIS10插XSD',N'202GIS10插XSE',N'202GIS10拔XSD',N'202GIS10拔XSE',10)
+END
+ELSE IF @产线代码 = N'DLQ'
+BEGIN
+ INSERT INTO @点位 VALUES
+ (N'DLQ',N'AC0605-01',N'GCB1',N'S000225',N'225GCB1插XSD',N'225GCB1插XSE',N'225GCB1拔XSD',N'225GCB1拔XSE',1),
+ (N'DLQ',N'AC0605-02',N'GCB2',N'S000226',N'226GCB2插XSD',N'226GCB2插XSE',N'226GCB2拔XSD',N'226GCB2拔XSE',2),
+ (N'DLQ',N'AC0605-03',N'GCB3',N'S000227',N'227GCB3插XSD',N'227GCB3插XSE',N'227GCB3拔XSD',N'227GCB3拔XSE',3),
+ (N'DLQ',N'AC0605-04',N'GCB4',N'S000228',N'228GCB4插XSD',N'228GCB4插XSE',N'228GCB4拔XSD',N'228GCB4拔XSE',4)
+END
+
+MERGE dbo.XD_TCS_AGV_点位配置 AS T
+USING @点位 AS S ON T.MES工位号 = S.MES工位号
+WHEN MATCHED THEN UPDATE SET
+ T.产线代码=S.产线代码,T.现场位置=S.现场位置,T.WorkStation=S.WorkStation,
+ T.插XSDProgramName=S.插XSD,T.插XSEProgramName=S.插XSE,
+ T.拔XSDProgramName=S.拔XSD,T.拔XSEProgramName=S.拔XSE,
+ T.排序=S.排序,T.是否启用=1,T.操作时间=GETDATE()
+WHEN NOT MATCHED THEN INSERT
+ (产线代码,MES工位号,现场位置,WorkStation,插XSDProgramName,插XSEProgramName,拔XSDProgramName,拔XSEProgramName,排序)
+ VALUES(S.产线代码,S.MES工位号,S.现场位置,S.WorkStation,S.插XSD,S.插XSE,S.拔XSD,S.拔XSE,S.排序);
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_配置_获取
+AS
+BEGIN
+ SET NOCOUNT ON
+ SELECT
+ TCS_BaseUrl = MAX(CASE WHEN 配置键=N'TCS_BaseUrl' THEN 配置值 END),
+ TCS_Enabled = MAX(CASE WHEN 配置键=N'TCS_Enabled' THEN 配置值 END),
+ McrGroup = MAX(CASE WHEN 配置键=N'McrGroup' THEN 配置值 END),
+ McrIp = MAX(CASE WHEN 配置键=N'McrIp' THEN 配置值 END),
+ TCS_OperatorUserId = MAX(CASE WHEN 配置键=N'TCS_OperatorUserId' THEN 配置值 END),
+ DefaultPriority = MAX(CASE WHEN 配置键=N'DefaultPriority' THEN 配置值 END),
+ HttpTimeoutSeconds = MAX(CASE WHEN 配置键=N'HttpTimeoutSeconds' THEN 配置值 END)
+ FROM dbo.XD_TCS_AGV_配置 WHERE 是否启用=1
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_点位配置_查询
+@MES工位号 nvarchar(50) = N''
+AS
+BEGIN
+ SET NOCOUNT ON
+ SELECT * FROM dbo.XD_TCS_AGV_点位配置
+ WHERE 是否启用=1 AND (@MES工位号=N'' OR MES工位号=@MES工位号)
+ ORDER BY 排序
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_型号动作映射_查询
+@产线代码 nvarchar(20),
+@产品型号 nvarchar(100),
+@任务动作 nvarchar(10)
+AS
+BEGIN
+ SET NOCOUNT ON
+ SELECT TOP 1 ID,产线代码,产品型号,任务动作,执行XSD,执行XSE,
+ CASE WHEN 执行XSD=1 AND 执行XSE=1 THEN N'XSD+XSE'
+ WHEN 执行XSD=1 THEN N'XSD' ELSE N'XSE' END 动作组合
+ FROM dbo.XD_TCS_AGV_型号动作映射
+ WHERE 产线代码=@产线代码 AND 产品型号=@产品型号
+ AND 任务动作=@任务动作 AND 是否启用=1
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_库位产品_查询
+@MES工位号 nvarchar(50)
+AS
+BEGIN
+ SET NOCOUNT ON
+ SELECT TOP 1 产品编号,产品型号
+ FROM dbo.XD_磨合_状态
+ WHERE 库位号=@MES工位号
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_任务_创建
+@外部任务编号 nvarchar(100),
+@原任务编号 nvarchar(100)=N'',
+@MES工位号 nvarchar(50),
+@任务动作 nvarchar(20),
+@产品型号 nvarchar(100)=N'',
+@动作组合 nvarchar(20)=N'',
+@ProgramName nvarchar(100)=N'',
+@任务类型 nvarchar(50),
+@参数 nvarchar(max),
+@优先级 int,
+@机器人组 nvarchar(50)=N'',
+@机器人IP nvarchar(50)=N'',
+@来源 nvarchar(20),
+@操作人 nvarchar(50)=N'',
+@是否通知PLC bit=0
+AS
+BEGIN
+ SET NOCOUNT ON
+ SET XACT_ABORT ON
+ BEGIN TRAN
+
+ IF NOT EXISTS(SELECT 1 FROM dbo.XD_TCS_AGV_点位配置 WHERE MES工位号=@MES工位号 AND 是否启用=1)
+ BEGIN
+ ROLLBACK
+ SELECT 2 result,N'工位未配置TCS点位' msg
+ RETURN
+ END
+
+ IF EXISTS(SELECT 1 FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@外部任务编号)
+ BEGIN
+ ROLLBACK
+ SELECT 2 result,N'外部任务编号已存在' msg
+ RETURN
+ END
+
+ IF ISNULL(@原任务编号,N'')<>N''
+ BEGIN
+ DECLARE @原任务工位 nvarchar(50),@原任务状态 int,@原处理状态 int,@原任务已复位 bit
+ SELECT @原任务工位=MES工位号,@原任务状态=任务状态,
+ @原处理状态=处理状态,@原任务已复位=已复位
+ FROM dbo.XD_TCS_AGV_任务 WITH(UPDLOCK,HOLDLOCK)
+ WHERE 外部任务编号=@原任务编号
+
+ IF @原任务工位 IS NULL
+ BEGIN
+ ROLLBACK
+ SELECT 2 result,N'原失败任务不存在' msg
+ RETURN
+ END
+ IF @原任务状态 NOT IN(4,5) OR ISNULL(@原处理状态,0)<>0
+ BEGIN
+ ROLLBACK
+ SELECT 2 result,N'原任务不是待处理的失败任务' msg
+ RETURN
+ END
+ IF ISNULL(@原任务已复位,0)<>1
+ BEGIN
+ ROLLBACK
+ SELECT 2 result,N'请先复位原失败任务对应的移动机器人' msg
+ RETURN
+ END
+ IF @原任务工位<>@MES工位号
+ BEGIN
+ ROLLBACK
+ SELECT 2 result,N'恢复任务工位必须与原失败任务一致' msg
+ RETURN
+ END
+ END
+
+ -- MES允许各工位独立下发任务,排队与执行顺序由TCS调度。
+
+ DECLARE @现场位置 nvarchar(50),@TCS站点 nvarchar(50)
+ SELECT @现场位置=现场位置,@TCS站点=WorkStation
+ FROM dbo.XD_TCS_AGV_点位配置 WHERE MES工位号=@MES工位号 AND 是否启用=1
+
+ INSERT INTO dbo.XD_TCS_AGV_任务
+ (外部任务编号,原任务编号,MES工位号,现场位置,TCS站点,任务动作,产品型号,动作组合,ProgramName,任务类型,参数,
+ 优先级,机器人组,机器人IP,来源,操作人,是否通知PLC)
+ VALUES
+ (@外部任务编号,NULLIF(@原任务编号,N''),@MES工位号,@现场位置,@TCS站点,@任务动作,NULLIF(@产品型号,N''),NULLIF(@动作组合,N''),NULLIF(@ProgramName,N''),
+ @任务类型,@参数,@优先级,@机器人组,@机器人IP,@来源,@操作人,@是否通知PLC)
+
+ DECLARE @ID bigint=SCOPE_IDENTITY()
+ COMMIT
+ SELECT 1 result,N'任务已创建' msg,@ID ID,@外部任务编号 外部任务编号
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_任务_下发结果更新
+@外部任务编号 nvarchar(100),
+@请求报文 nvarchar(max)=N'',
+@响应报文 nvarchar(max)=N'',
+@是否成功 bit,
+@错误信息 nvarchar(2000)=N''
+AS
+BEGIN
+ SET NOCOUNT ON
+ UPDATE dbo.XD_TCS_AGV_任务 SET
+ 请求报文=@请求报文,响应报文=@响应报文,
+ 任务状态=CASE WHEN @是否成功=1 THEN 0 ELSE 4 END,
+ 状态说明=CASE WHEN @是否成功=1 THEN N'未开始' ELSE N'任务错误' END,
+ 错误信息=NULLIF(@错误信息,N''),更新时间=GETDATE()
+ WHERE 外部任务编号=@外部任务编号
+
+ SELECT CASE WHEN @@ROWCOUNT=1 THEN 1 ELSE 2 END result
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_任务_状态更新
+@外部任务编号 nvarchar(100),
+@任务状态 int,
+@机器人IP nvarchar(50)=N'',
+@任务类型 nvarchar(50)=N'',
+@开始时间 nvarchar(50)=N'',
+@结束时间 nvarchar(50)=N'',
+@备注 nvarchar(1000)=N''
+AS
+BEGIN
+ SET NOCOUNT ON
+ IF @任务状态 NOT IN(0,1,2,3,4,5)
+ BEGIN
+ SELECT 2 result,N'任务状态不正确' msg
+ RETURN
+ END
+
+ IF NOT EXISTS(SELECT 1 FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@外部任务编号)
+ BEGIN
+ SELECT 2 result,N'外部任务编号不存在' msg
+ RETURN
+ END
+
+ DECLARE @原状态 int
+ SELECT @原状态=任务状态 FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@外部任务编号
+
+ IF (@原状态 IN(2,3,5) AND @任务状态<>@原状态)
+ OR (@原状态=4 AND @任务状态 NOT IN(4,5))
+ BEGIN
+ SELECT 1 result,N'已忽略结束状态变更' msg,MES工位号,任务动作,是否通知PLC,PLC已通知
+ FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@外部任务编号
+ RETURN
+ END
+
+ UPDATE dbo.XD_TCS_AGV_任务 SET
+ 任务状态=@任务状态,
+ 状态说明=CASE @任务状态 WHEN 0 THEN N'未开始' WHEN 1 THEN N'进行中' WHEN 2 THEN N'已完成' WHEN 3 THEN N'已取消' WHEN 4 THEN N'任务错误' ELSE N'任务错误(已处理)' END,
+ 机器人IP=CASE WHEN @机器人IP=N'' THEN 机器人IP ELSE @机器人IP END,
+ 任务类型=CASE WHEN @任务类型=N'' THEN 任务类型 ELSE @任务类型 END,
+ 开始时间=CASE WHEN @开始时间=N'' THEN 开始时间 ELSE @开始时间 END,
+ 结束时间=CASE WHEN @结束时间=N'' THEN 结束时间 ELSE @结束时间 END,
+ TCS备注=NULLIF(@备注,N''),
+ 错误信息=CASE WHEN @任务状态=4 THEN NULLIF(@备注,N'') ELSE 错误信息 END,
+ 已复位=CASE WHEN @任务状态=5 THEN 1 ELSE 已复位 END,
+ 更新时间=GETDATE()
+ WHERE 外部任务编号=@外部任务编号
+
+ SELECT 1 result,N'状态已更新' msg,MES工位号,任务动作,是否通知PLC,PLC已通知
+ FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@外部任务编号
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_任务_PLC通知完成
+@外部任务编号 nvarchar(100)
+AS
+BEGIN
+ SET NOCOUNT ON
+ UPDATE dbo.XD_TCS_AGV_任务 SET PLC已通知=1,更新时间=GETDATE()
+ WHERE 外部任务编号=@外部任务编号 AND 任务状态=2
+ SELECT CASE WHEN @@ROWCOUNT=1 THEN 1 ELSE 2 END result
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_任务_获取
+@外部任务编号 nvarchar(100)
+AS
+BEGIN
+ SET NOCOUNT ON
+ SELECT * FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@外部任务编号
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_任务_人工处理
+@外部任务编号 nvarchar(100),
+@处理人 nvarchar(50)=N'',
+@处理说明 nvarchar(1000)=N''
+AS
+BEGIN
+ SET NOCOUNT ON
+ IF NOT EXISTS(SELECT 1 FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@外部任务编号 AND 任务状态 IN(4,5))
+ BEGIN
+ SELECT 2 result,N'仅任务错误状态可以标记人工处理' msg
+ RETURN
+ END
+
+
+ IF NOT EXISTS(SELECT 1 FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@外部任务编号 AND 已复位=1)
+ BEGIN
+ SELECT 2 result,N'请先复位移动机器人再完成人工处理' msg
+ RETURN
+ END
+
+ UPDATE dbo.XD_TCS_AGV_任务 SET 处理状态=1,处理人=@处理人,处理时间=GETDATE(),处理说明=@处理说明,更新时间=GETDATE()
+ WHERE 外部任务编号=@外部任务编号
+ SELECT 1 result,N'已标记人工处理' msg
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_任务_复位标记
+@外部任务编号 nvarchar(100)
+AS
+BEGIN
+ SET NOCOUNT ON
+ UPDATE dbo.XD_TCS_AGV_任务 SET 已复位=1,更新时间=GETDATE()
+ WHERE 外部任务编号=@外部任务编号 AND 任务状态 IN(4,5)
+ SELECT CASE WHEN @@ROWCOUNT=1 THEN 1 ELSE 2 END result,
+ CASE WHEN @@ROWCOUNT=1 THEN N'复位状态已记录' ELSE N'未找到对应失败任务' END msg
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_任务_本地状态设置
+@外部任务编号 nvarchar(100),
+@任务状态 int,
+@状态说明 nvarchar(50),
+@错误信息 nvarchar(2000)=N''
+AS
+BEGIN
+ SET NOCOUNT ON
+ UPDATE dbo.XD_TCS_AGV_任务 SET 任务状态=@任务状态,状态说明=@状态说明,
+ 错误信息=NULLIF(@错误信息,N''),更新时间=GETDATE()
+ WHERE 外部任务编号=@外部任务编号
+ SELECT CASE WHEN @@ROWCOUNT=1 THEN 1 ELSE 2 END result
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_任务_分页查询
+@开始时间 nvarchar(50)=N'',
+@结束时间 nvarchar(50)=N'',
+@MES工位号 nvarchar(50)=N'',
+@外部任务编号 nvarchar(100)=N'',
+@任务动作 nvarchar(20)=N'',
+@任务状态 nvarchar(10)=N'',
+@来源 nvarchar(20)=N'',
+@PageCurrent int=1,
+@PageSize int=20,
+@PageCount int output,
+@ItemCount int output
+AS
+BEGIN
+ SET NOCOUNT ON
+ DECLARE @where nvarchar(2000)=N'1=1'
+ IF @开始时间<>N'' SET @where+=N' AND 创建时间>='''+REPLACE(@开始时间,'''','''''')+N''''
+ IF @结束时间<>N'' SET @where+=N' AND 创建时间<='''+REPLACE(@结束时间,'''','''''')+N''''
+ IF @MES工位号<>N'' SET @where+=N' AND MES工位号='''+REPLACE(@MES工位号,'''','''''')+N''''
+ IF @外部任务编号<>N'' SET @where+=N' AND 外部任务编号 LIKE ''%'+REPLACE(@外部任务编号,'''','''''')+N'%'''
+ IF @任务动作<>N'' SET @where+=N' AND 任务动作='''+REPLACE(@任务动作,'''','''''')+N''''
+ IF @任务状态<>N'' SET @where+=N' AND 任务状态='+CONVERT(nvarchar(10),CONVERT(int,@任务状态))
+ IF @来源<>N'' SET @where+=N' AND 来源='''+REPLACE(@来源,'''','''''')+N''''
+
+ EXEC sp_xt_pagesplit N'XD_TCS_AGV_任务',N'ID',@where,
+ N'ID,外部任务编号,原任务编号,MES工位号,现场位置,TCS站点,任务动作,产品型号,动作组合,ProgramName,任务类型,优先级,机器人组,机器人IP,来源,操作人,任务状态,状态说明,开始时间,结束时间,TCS备注,错误信息,是否通知PLC,PLC已通知,处理状态,已复位,处理人,处理时间,处理说明,创建时间,更新时间',
+ N'ID DESC',@PageCurrent,@PageSize,@PageCount output,@ItemCount output
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_任务_详情查询
+@外部任务编号 nvarchar(100)
+AS
+BEGIN
+ SET NOCOUNT ON
+ SELECT * FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@外部任务编号
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_任务_当前查询
+AS
+BEGIN
+ SET NOCOUNT ON
+ SELECT TOP 1 * FROM dbo.XD_TCS_AGV_任务
+ WHERE 任务状态=1
+ ORDER BY 更新时间 DESC,ID DESC
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_任务_概览查询
+AS
+BEGIN
+ SET NOCOUNT ON
+ SELECT
+ ISNULL(SUM(CASE WHEN 任务状态=0 THEN 1 ELSE 0 END),0) 待执行数量,
+ ISNULL(SUM(CASE WHEN 任务状态=1 THEN 1 ELSE 0 END),0) 进行中数量,
+ ISNULL(SUM(CASE WHEN 任务状态 IN(4,5) AND 处理状态=0 THEN 1 ELSE 0 END),0) 失败待处理数量
+ FROM dbo.XD_TCS_AGV_任务
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_工位日志_分页查询
+@MES工位号 nvarchar(50)=N'',
+@日志类型 nvarchar(50)=N'',
+@日志文本 nvarchar(200)=N'',
+@开始时间 nvarchar(50)=N'',
+@结束时间 nvarchar(50)=N'',
+@PageCurrent int=1,
+@PageSize int=20,
+@PageCount int OUTPUT,
+@ItemCount int OUTPUT
+AS
+BEGIN
+ SET NOCOUNT ON
+
+ IF ISNULL(@PageCurrent,0)<1 SET @PageCurrent=1
+ IF ISNULL(@PageSize,0)<1 SET @PageSize=20
+
+ DECLARE @开始 datetime=TRY_CONVERT(datetime,NULLIF(@开始时间,N''))
+ DECLARE @结束 datetime=TRY_CONVERT(datetime,NULLIF(@结束时间,N''))
+
+ SELECT @ItemCount=COUNT(1)
+ FROM dbo.MES_系统日志 L
+ WHERE EXISTS
+ (
+ SELECT 1 FROM dbo.XD_TCS_AGV_点位配置 P
+ WHERE P.MES工位号=L.工位号 AND P.是否启用=1
+ )
+ AND (@MES工位号=N'' OR L.工位号=@MES工位号)
+ AND (@日志类型=N'' OR L.日志类型=@日志类型)
+ AND (@日志文本=N'' OR L.日志文本 LIKE N'%'+@日志文本+N'%')
+ AND (@开始 IS NULL OR L.操作时间>=@开始)
+ AND (@结束 IS NULL OR L.操作时间<=@结束)
+
+ SET @PageCount=CASE WHEN @ItemCount=0 THEN 0 ELSE CEILING(@ItemCount*1.0/@PageSize) END
+
+ SELECT L.ID,L.工位号,L.日志类型,L.日志文本,L.操作时间
+ FROM dbo.MES_系统日志 L
+ WHERE EXISTS
+ (
+ SELECT 1 FROM dbo.XD_TCS_AGV_点位配置 P
+ WHERE P.MES工位号=L.工位号 AND P.是否启用=1
+ )
+ AND (@MES工位号=N'' OR L.工位号=@MES工位号)
+ AND (@日志类型=N'' OR L.日志类型=@日志类型)
+ AND (@日志文本=N'' OR L.日志文本 LIKE N'%'+@日志文本+N'%')
+ AND (@开始 IS NULL OR L.操作时间>=@开始)
+ AND (@结束 IS NULL OR L.操作时间<=@结束)
+ ORDER BY L.操作时间 DESC,L.ID DESC
+ OFFSET (@PageCurrent-1)*@PageSize ROWS FETCH NEXT @PageSize ROWS ONLY
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_位置_更新
+@机器人IP nvarchar(50)=N'',
+@当前站点 nvarchar(100)=N'',
+@目标站点 nvarchar(100)=N'',
+@原始报文 nvarchar(max)=N''
+AS
+BEGIN
+ SET NOCOUNT ON
+ IF EXISTS(SELECT 1 FROM dbo.XD_TCS_AGV_位置状态 WHERE ID=1)
+ UPDATE dbo.XD_TCS_AGV_位置状态 SET 机器人IP=@机器人IP,当前站点=@当前站点,目标站点=@目标站点,原始报文=@原始报文,更新时间=GETDATE() WHERE ID=1
+ ELSE
+ INSERT INTO dbo.XD_TCS_AGV_位置状态(ID,机器人IP,当前站点,目标站点,原始报文) VALUES(1,@机器人IP,@当前站点,@目标站点,@原始报文)
+ SELECT 1 result
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_位置_查询
+AS
+BEGIN
+ SET NOCOUNT ON
+ SELECT * FROM dbo.XD_TCS_AGV_位置状态 WHERE ID=1
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_操作记录_增加
+@外部任务编号 nvarchar(100)=N'',
+@MES工位号 nvarchar(50)=N'',
+@操作类型 nvarchar(50),
+@操作人 nvarchar(50)=N'',
+@请求报文 nvarchar(max)=N'',
+@响应报文 nvarchar(max)=N'',
+@是否成功 bit,
+@操作结果 nvarchar(1000)=N''
+AS
+BEGIN
+ SET NOCOUNT ON
+ INSERT INTO dbo.XD_TCS_AGV_操作记录(外部任务编号,MES工位号,操作类型,操作人,请求报文,响应报文,是否成功,操作结果)
+ VALUES(NULLIF(@外部任务编号,N''),NULLIF(@MES工位号,N''),@操作类型,@操作人,@请求报文,@响应报文,@是否成功,@操作结果)
+ SELECT 1 result
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_操作记录_分页查询
+@开始时间 nvarchar(50)=N'',
+@结束时间 nvarchar(50)=N'',
+@操作类型 nvarchar(50)=N'',
+@PageCurrent int=1,
+@PageSize int=20,
+@PageCount int output,
+@ItemCount int output
+AS
+BEGIN
+ SET NOCOUNT ON
+ DECLARE @where nvarchar(1000)=N'1=1'
+ IF @开始时间<>N'' SET @where+=N' AND 操作时间>='''+REPLACE(@开始时间,'''','''''')+N''''
+ IF @结束时间<>N'' SET @where+=N' AND 操作时间<='''+REPLACE(@结束时间,'''','''''')+N''''
+ IF @操作类型<>N'' SET @where+=N' AND 操作类型='''+REPLACE(@操作类型,'''','''''')+N''''
+ EXEC sp_xt_pagesplit N'XD_TCS_AGV_操作记录',N'ID',@where,
+ N'ID,外部任务编号,MES工位号,操作类型,操作人,是否成功,操作结果,操作时间',
+ N'ID DESC',@PageCurrent,@PageSize,@PageCount output,@ItemCount output
+END
+GO
+
+CREATE OR ALTER PROCEDURE dbo.XD_TCS_AGV_操作记录_任务查询
+@外部任务编号 nvarchar(100)
+AS
+BEGIN
+ SET NOCOUNT ON
+ SELECT ID,外部任务编号,MES工位号,操作类型,操作人,是否成功,操作结果,操作时间
+ FROM dbo.XD_TCS_AGV_操作记录
+ WHERE 外部任务编号=@外部任务编号
+ ORDER BY ID DESC
+END
+GO
diff --git a/DatabaseScripts/XD_TCS_AGV状态机测试.sql b/DatabaseScripts/XD_TCS_AGV状态机测试.sql
new file mode 100644
index 0000000..45d2b05
--- /dev/null
+++ b/DatabaseScripts/XD_TCS_AGV状态机测试.sql
@@ -0,0 +1,98 @@
+SET NOCOUNT ON
+
+DECLARE @前缀 nvarchar(50)=N'UT_AGV_STATE_'
+DECLARE @首任务 nvarchar(100)=@前缀+N'BASE'
+DECLARE @并发任务 nvarchar(100)=@前缀+N'SECOND'
+DECLARE @未复位恢复 nvarchar(100)=@前缀+N'NORESET'
+DECLARE @跨工位恢复 nvarchar(100)=@前缀+N'WRONG_STATION'
+DECLARE @正确恢复 nvarchar(100)=@前缀+N'RECOVERY'
+DECLARE @失败未处理阻断 nvarchar(100)=@前缀+N'BLOCKED'
+DECLARE @处理后任务 nvarchar(100)=@前缀+N'AFTER_HANDLE'
+DECLARE @失败断言数 int=0
+
+DELETE FROM dbo.XD_TCS_AGV_操作记录 WHERE 外部任务编号 LIKE @前缀+N'%'
+DELETE FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号 LIKE @前缀+N'%'
+
+PRINT N'[1] 创建首个任务'
+EXEC dbo.XD_TCS_AGV_任务_创建
+ @首任务,N'',N'AC0909-01',N'插',N'',N'nav_action_program',
+ N'[{"workStation":"S000214"},{"programName":"214GIS1插XSD"},{"programName":"214GIS1插XSE"}]',
+ 1,N'G001',N'10.146.79.9',N'UNIT_TEST',N'状态机测试',0
+IF NOT EXISTS(SELECT 1 FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@首任务)
+ SET @失败断言数+=1
+
+PRINT N'[2] 单车活动任务拦截'
+EXEC dbo.XD_TCS_AGV_任务_创建
+ @并发任务,N'',N'AC0909-02',N'插',N'',N'nav_action_program',N'[{"workStation":"S000215"}]',
+ 1,N'G001',N'10.146.79.9',N'UNIT_TEST',N'状态机测试',0
+IF EXISTS(SELECT 1 FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@并发任务)
+ SET @失败断言数+=1
+
+EXEC dbo.XD_TCS_AGV_任务_本地状态设置 @首任务,4,N'任务错误',N'单元测试失败'
+
+PRINT N'[3] 未复位恢复任务拦截'
+EXEC dbo.XD_TCS_AGV_任务_创建
+ @未复位恢复,@首任务,N'AC0909-01',N'插-单步',N'214GIS1插XSD',N'program',
+ N'[{"programName":"214GIS1插XSD"}]',1,N'G001',N'10.146.79.9',N'UNIT_TEST',N'状态机测试',0
+IF EXISTS(SELECT 1 FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@未复位恢复)
+ SET @失败断言数+=1
+
+PRINT N'[4] 复位标记与跨工位恢复拦截'
+EXEC dbo.XD_TCS_AGV_任务_复位标记 @首任务
+EXEC dbo.XD_TCS_AGV_任务_状态更新
+ @首任务,5,N'10.146.79.9',N'nav_action_program',N'',N'',N'任务错误已处理'
+IF NOT EXISTS(SELECT 1 FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@首任务 AND 任务状态=5 AND 已复位=1)
+ SET @失败断言数+=1
+EXEC dbo.XD_TCS_AGV_任务_创建
+ @跨工位恢复,@首任务,N'AC0909-02',N'插-单步',N'215GIS2插XSD',N'program',
+ N'[{"programName":"215GIS2插XSD"}]',1,N'G001',N'10.146.79.9',N'UNIT_TEST',N'状态机测试',0
+IF EXISTS(SELECT 1 FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@跨工位恢复)
+ SET @失败断言数+=1
+
+PRINT N'[5] 复位后同工位恢复放行'
+EXEC dbo.XD_TCS_AGV_任务_创建
+ @正确恢复,@首任务,N'AC0909-01',N'插-单步',N'214GIS1插XSD',N'program',
+ N'[{"programName":"214GIS1插XSD"}]',1,N'G001',N'10.146.79.9',N'UNIT_TEST',N'状态机测试',0
+IF NOT EXISTS(SELECT 1 FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@正确恢复)
+ SET @失败断言数+=1
+EXEC dbo.XD_TCS_AGV_任务_状态更新
+ @正确恢复,2,N'10.146.79.9',N'program',N'2026-07-13 10:00:00',N'2026-07-13 10:01:00',N''
+
+PRINT N'[6] 原失败任务未处理时继续阻断'
+EXEC dbo.XD_TCS_AGV_任务_创建
+ @失败未处理阻断,N'',N'AC0909-02',N'插',N'',N'nav_action_program',N'[{"workStation":"S000215"}]',
+ 1,N'G001',N'10.146.79.9',N'UNIT_TEST',N'状态机测试',0
+IF EXISTS(SELECT 1 FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@失败未处理阻断)
+ SET @失败断言数+=1
+
+PRINT N'[7] 人工处理后新任务放行'
+EXEC dbo.XD_TCS_AGV_任务_人工处理 @首任务,N'状态机测试',N'恢复任务已完成'
+EXEC dbo.XD_TCS_AGV_任务_状态更新 @首任务,2,N'',N'',N'',N'',N'迟到完成'
+IF EXISTS(SELECT 1 FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@首任务 AND 任务状态<>5)
+ SET @失败断言数+=1
+EXEC dbo.XD_TCS_AGV_任务_创建
+ @处理后任务,N'',N'AC0909-02',N'拔',N'',N'nav_action_program',N'[{"workStation":"S000215"}]',
+ 1,N'G001',N'10.146.79.9',N'UNIT_TEST',N'状态机测试',0
+IF NOT EXISTS(SELECT 1 FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@处理后任务)
+ SET @失败断言数+=1
+
+PRINT N'[8] 未复位失败任务不能人工处理,终态不能改写'
+EXEC dbo.XD_TCS_AGV_任务_本地状态设置 @处理后任务,4,N'任务错误',N'终态测试'
+EXEC dbo.XD_TCS_AGV_任务_人工处理 @处理后任务,N'状态机测试',N'不应成功'
+IF EXISTS(SELECT 1 FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@处理后任务 AND 处理状态=1)
+ SET @失败断言数+=1
+EXEC dbo.XD_TCS_AGV_任务_状态更新 @处理后任务,2,N'',N'',N'',N'',N'迟到完成'
+IF EXISTS(SELECT 1 FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号=@处理后任务 AND 任务状态<>4)
+ SET @失败断言数+=1
+
+SELECT @失败断言数 AS 失败断言数,
+ (SELECT COUNT(*) FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号 LIKE @前缀+N'%') AS 测试任务数
+
+DELETE FROM dbo.XD_TCS_AGV_操作记录 WHERE 外部任务编号 LIKE @前缀+N'%'
+DELETE FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号 LIKE @前缀+N'%'
+
+IF @失败断言数<>0
+ RAISERROR(N'AGV数据库状态机测试失败',16,1)
+
+SELECT COUNT(*) AS 清理后测试任务数
+FROM dbo.XD_TCS_AGV_任务 WHERE 外部任务编号 LIKE @前缀+N'%'
diff --git a/DatabaseScripts/XD_电能表谐波数据.sql b/DatabaseScripts/XD_电能表谐波数据.sql
deleted file mode 100644
index 8c6308a..0000000
--- a/DatabaseScripts/XD_电能表谐波数据.sql
+++ /dev/null
@@ -1,173 +0,0 @@
-IF OBJECT_ID(N'dbo.XD_电能表谐波数据', N'U') IS NULL
-BEGIN
- CREATE TABLE dbo.XD_电能表谐波数据
- (
- ID int IDENTITY(1,1) NOT NULL,
- 工位号 nvarchar(50) NULL,
- A相电压总谐波畸变 float NULL,
- B相电压总谐波畸变 float NULL,
- C相电压总谐波畸变 float NULL,
- 系统电压总谐波畸变 float NULL,
- A相电流总谐波畸变 float NULL,
- B相电流总谐波畸变 float NULL,
- C相电流总谐波畸变 float NULL,
- 系统电流总谐波畸变 float NULL,
- A相电压奇次谐波畸变 float NULL,
- B相电压奇次谐波畸变 float NULL,
- C相电压奇次谐波畸变 float NULL,
- A相电流奇次谐波畸变 float NULL,
- B相电流奇次谐波畸变 float NULL,
- C相电流奇次谐波畸变 float NULL,
- A相电压偶次谐波畸变 float NULL,
- B相电压偶次谐波畸变 float NULL,
- C相电压偶次谐波畸变 float NULL,
- A相电流偶次谐波畸变 float NULL,
- B相电流偶次谐波畸变 float NULL,
- C相电流偶次谐波畸变 float NULL,
- 操作时间 datetime NULL CONSTRAINT DF_XD_电能表谐波数据_操作时间 DEFAULT (GETDATE()),
- CONSTRAINT PK_XD_电能表谐波数据 PRIMARY KEY CLUSTERED (ID ASC)
- );
-END
-GO
-
-IF NOT EXISTS (
- SELECT 1
- FROM sys.indexes
- WHERE name = N'IX_XD_电能表谐波数据_工位号_操作时间'
- AND object_id = OBJECT_ID(N'dbo.XD_电能表谐波数据')
-)
-BEGIN
- CREATE NONCLUSTERED INDEX IX_XD_电能表谐波数据_工位号_操作时间
- ON dbo.XD_电能表谐波数据 (工位号 ASC, 操作时间 DESC);
-END
-GO
-
-IF OBJECT_ID(N'dbo.XD_电能表谐波数据_增加', N'P') IS NOT NULL
- DROP PROCEDURE dbo.XD_电能表谐波数据_增加;
-GO
-
-CREATE PROCEDURE dbo.XD_电能表谐波数据_增加
- @工位号 nvarchar(50) = N'',
- @A相电压总谐波畸变 float = NULL,
- @B相电压总谐波畸变 float = NULL,
- @C相电压总谐波畸变 float = NULL,
- @系统电压总谐波畸变 float = NULL,
- @A相电流总谐波畸变 float = NULL,
- @B相电流总谐波畸变 float = NULL,
- @C相电流总谐波畸变 float = NULL,
- @系统电流总谐波畸变 float = NULL,
- @A相电压奇次谐波畸变 float = NULL,
- @B相电压奇次谐波畸变 float = NULL,
- @C相电压奇次谐波畸变 float = NULL,
- @A相电流奇次谐波畸变 float = NULL,
- @B相电流奇次谐波畸变 float = NULL,
- @C相电流奇次谐波畸变 float = NULL,
- @A相电压偶次谐波畸变 float = NULL,
- @B相电压偶次谐波畸变 float = NULL,
- @C相电压偶次谐波畸变 float = NULL,
- @A相电流偶次谐波畸变 float = NULL,
- @B相电流偶次谐波畸变 float = NULL,
- @C相电流偶次谐波畸变 float = NULL
-AS
-BEGIN
- SET NOCOUNT ON;
-
- IF NULLIF(LTRIM(RTRIM(@工位号)), N'') IS NULL
- RETURN;
-
- -- 电表返回 -1 表示无效数据;任一谐波点无效时,本次整条采集不入库。
- IF @A相电压总谐波畸变 = -1
- OR @B相电压总谐波畸变 = -1
- OR @C相电压总谐波畸变 = -1
- OR @系统电压总谐波畸变 = -1
- OR @A相电流总谐波畸变 = -1
- OR @B相电流总谐波畸变 = -1
- OR @C相电流总谐波畸变 = -1
- OR @系统电流总谐波畸变 = -1
- OR @A相电压奇次谐波畸变 = -1
- OR @B相电压奇次谐波畸变 = -1
- OR @C相电压奇次谐波畸变 = -1
- OR @A相电流奇次谐波畸变 = -1
- OR @B相电流奇次谐波畸变 = -1
- OR @C相电流奇次谐波畸变 = -1
- OR @A相电压偶次谐波畸变 = -1
- OR @B相电压偶次谐波畸变 = -1
- OR @C相电压偶次谐波畸变 = -1
- OR @A相电流偶次谐波畸变 = -1
- OR @B相电流偶次谐波畸变 = -1
- OR @C相电流偶次谐波畸变 = -1
- RETURN;
-
- -- 谐波畸变允许为 0;只有全部点位都为空时跳过保存。
- IF @A相电压总谐波畸变 IS NULL
- AND @B相电压总谐波畸变 IS NULL
- AND @C相电压总谐波畸变 IS NULL
- AND @系统电压总谐波畸变 IS NULL
- AND @A相电流总谐波畸变 IS NULL
- AND @B相电流总谐波畸变 IS NULL
- AND @C相电流总谐波畸变 IS NULL
- AND @系统电流总谐波畸变 IS NULL
- AND @A相电压奇次谐波畸变 IS NULL
- AND @B相电压奇次谐波畸变 IS NULL
- AND @C相电压奇次谐波畸变 IS NULL
- AND @A相电流奇次谐波畸变 IS NULL
- AND @B相电流奇次谐波畸变 IS NULL
- AND @C相电流奇次谐波畸变 IS NULL
- AND @A相电压偶次谐波畸变 IS NULL
- AND @B相电压偶次谐波畸变 IS NULL
- AND @C相电压偶次谐波畸变 IS NULL
- AND @A相电流偶次谐波畸变 IS NULL
- AND @B相电流偶次谐波畸变 IS NULL
- AND @C相电流偶次谐波畸变 IS NULL
- RETURN;
-
- INSERT INTO dbo.XD_电能表谐波数据
- (
- 工位号,
- A相电压总谐波畸变,
- B相电压总谐波畸变,
- C相电压总谐波畸变,
- 系统电压总谐波畸变,
- A相电流总谐波畸变,
- B相电流总谐波畸变,
- C相电流总谐波畸变,
- 系统电流总谐波畸变,
- A相电压奇次谐波畸变,
- B相电压奇次谐波畸变,
- C相电压奇次谐波畸变,
- A相电流奇次谐波畸变,
- B相电流奇次谐波畸变,
- C相电流奇次谐波畸变,
- A相电压偶次谐波畸变,
- B相电压偶次谐波畸变,
- C相电压偶次谐波畸变,
- A相电流偶次谐波畸变,
- B相电流偶次谐波畸变,
- C相电流偶次谐波畸变
- )
- VALUES
- (
- @工位号,
- @A相电压总谐波畸变 / 1000.0,
- @B相电压总谐波畸变 / 1000.0,
- @C相电压总谐波畸变 / 1000.0,
- @系统电压总谐波畸变 / 1000.0,
- @A相电流总谐波畸变 / 1000.0,
- @B相电流总谐波畸变 / 1000.0,
- @C相电流总谐波畸变 / 1000.0,
- @系统电流总谐波畸变 / 1000.0,
- @A相电压奇次谐波畸变 / 1000.0,
- @B相电压奇次谐波畸变 / 1000.0,
- @C相电压奇次谐波畸变 / 1000.0,
- @A相电流奇次谐波畸变 / 1000.0,
- @B相电流奇次谐波畸变 / 1000.0,
- @C相电流奇次谐波畸变 / 1000.0,
- @A相电压偶次谐波畸变 / 1000.0,
- @B相电压偶次谐波畸变 / 1000.0,
- @C相电压偶次谐波畸变 / 1000.0,
- @A相电流偶次谐波畸变 / 1000.0,
- @B相电流偶次谐波畸变 / 1000.0,
- @C相电流偶次谐波畸变 / 1000.0
- );
-END
-GO
diff --git a/Interface_WebAPI/SlMesDbIterface/ExternalDataSync.csproj b/Interface_WebAPI/SlMesDbIterface/ExternalDataSync.csproj
index 812ee07..b1886cb 100644
--- a/Interface_WebAPI/SlMesDbIterface/ExternalDataSync.csproj
+++ b/Interface_WebAPI/SlMesDbIterface/ExternalDataSync.csproj
@@ -212,8 +212,14 @@
+
+
+
+
+
+
@@ -519,4 +525,4 @@
-
\ No newline at end of file
+
diff --git a/Interface_WebAPI/SlMesDbIterface/WebAPI/AGV/LGAGV_BusinessLogic.cs b/Interface_WebAPI/SlMesDbIterface/WebAPI/AGV/LGAGV_BusinessLogic.cs
new file mode 100644
index 0000000..37f142b
--- /dev/null
+++ b/Interface_WebAPI/SlMesDbIterface/WebAPI/AGV/LGAGV_BusinessLogic.cs
@@ -0,0 +1,405 @@
+using System;
+using System.Configuration;
+using System.Data;
+using System.Data.SqlClient;
+using System.Web.Http;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using MisDataSaveDate.Helper;
+using static MisDataSaveDate.Helper.ApiLogHelper;
+using WebApi;
+using MesServerWork;
+
+namespace MisDataSaveDate
+{
+ ///
+ /// 临工AGV接口业务处理。
+ ///
+ public class LGAGV_BusinessLogic
+ {
+ private const string DirectionMesToAgv = "MES_TO_AGV";
+ private const string DirectionAgvToMes = "AGV_TO_MES";
+
+ ///
+ /// MES调用临工AGV两点任务接口。
+ ///
+ public static LGAGV_CallResult CallAddTask(LGAGV_AddTaskRequest request)
+ {
+ if (request == null)
+ {
+ throw new Exception("AGV任务参数不能为空");
+ }
+ if (string.IsNullOrWhiteSpace(request.requestTaskId))
+ {
+ throw new Exception("任务ID(requestTaskId)不能为空");
+ }
+ if (string.IsNullOrWhiteSpace(request.startPosition))
+ {
+ throw new Exception("起点位置(startPosition)不能为空");
+ }
+ if (string.IsNullOrWhiteSpace(request.endPosition))
+ {
+ throw new Exception("终点位置(endPosition)不能为空");
+ }
+
+ return PostToAgv("AddTaskFromMes", "/api/mes/AddTaskFromMes", request,
+ request.requestTaskId, null, request.endPosition, null, request.materialCode, null);
+ }
+
+ ///
+ /// MES调用临工AGV三点任务接口。
+ ///
+ public static LGAGV_CallResult CallAddTask2(LGAGV_AddTask2Request request)
+ {
+ if (request == null)
+ {
+ throw new Exception("AGV任务参数不能为空");
+ }
+ if (string.IsNullOrWhiteSpace(request.requestTaskId))
+ {
+ throw new Exception("任务ID(requestTaskId)不能为空");
+ }
+ if (string.IsNullOrWhiteSpace(request.startPosition))
+ {
+ throw new Exception("起点位置(startPosition)不能为空");
+ }
+ if (string.IsNullOrWhiteSpace(request.midPosition))
+ {
+ throw new Exception("中间位置(midPosition)不能为空");
+ }
+ if (string.IsNullOrWhiteSpace(request.endPosition))
+ {
+ throw new Exception("终点位置(endPosition)不能为空");
+ }
+
+ return PostToAgv("AddTaskFromMes2", "/api/mes/AddTaskFromMes2", request,
+ request.requestTaskId, null, request.endPosition, null, request.materialCode, null);
+ }
+
+ ///
+ /// MES调用临工AGV放行接口。
+ ///
+ public static LGAGV_CallResult CallAllowLeave(LGAGV_AllowLeaveRequest request)
+ {
+ if (request == null)
+ {
+ throw new Exception("放行参数不能为空");
+ }
+ if (string.IsNullOrWhiteSpace(request.requestId))
+ {
+ throw new Exception("请求ID(requestId)不能为空");
+ }
+ if (string.IsNullOrWhiteSpace(request.position))
+ {
+ throw new Exception("位置(position)不能为空");
+ }
+
+ request.allowLeave = 1;
+ return PostToAgv("AllowLeave", "/api/mes/AllowLeave", request,
+ null, request.requestId, request.position, null, null, request.agvNo);
+ }
+
+ ///
+ /// 处理临工AGV到达/离开回调。
+ ///
+ public static string ProcessStatusUpdate(string url, [FromBody] JObject jobj)
+ {
+ var result = LGAGV_Response.Success();
+ string requestText = jobj?.ToString() ?? "";
+ SaveLog_Interface_Request(url, 2, requestText, out int aid);
+
+ LGAGV_StatusUpdateRequest request = null;
+ try
+ {
+ if (jobj == null)
+ {
+ throw new Exception("请求参数不能为空");
+ }
+
+ request = JsonConvert.DeserializeObject(requestText);
+ if (request == null)
+ {
+ throw new Exception("请求参数格式不正确");
+ }
+ if (string.IsNullOrWhiteSpace(request.requestId))
+ {
+ throw new Exception("请求ID(requestId)不能为空");
+ }
+ if (string.IsNullOrWhiteSpace(request.position))
+ {
+ throw new Exception("位置(position)不能为空");
+ }
+ if (request.type != 1 && request.type != 2)
+ {
+ throw new Exception("类型(type)只支持1到达、2离开");
+ }
+
+ string procedureName = request.type == 1 ? "LG_AGV_库位Moby_AGV到达" : "LG_AGV_库位Moby_AGV离开";
+ var param = new SqlParameter[]
+ {
+ new SqlParameter("@requestId", request.requestId ?? ""),
+ new SqlParameter("@position", request.position ?? ""),
+ new SqlParameter("@materialCode", request.materialCode ?? ""),
+ new SqlParameter("@agvNo", request.agvNo ?? "")
+ };
+ ExecuteResultProcedure(procedureName, ref param);
+ WriteOp10PlcStatus(request);
+
+ SaveInterfaceRecord("StatusUpdate", DirectionAgvToMes, request.position, request.position,
+ null, request.requestId, request.position, request.type, request.materialCode, request.agvNo,
+ "", requestText, "", 1, "");
+
+ string typeDesc = request.type == 1 ? "到达" : "离开";
+ SaveMesLog("LGAGV_StatusUpdate", $"临工AGV{typeDesc}:位置={request.position}, requestId={request.requestId}, AGV={request.agvNo}", MesLogType.INFO);
+ }
+ catch (Exception ex)
+ {
+ result = LGAGV_Response.Fail(ex.Message);
+ SaveInterfaceRecord("StatusUpdate", DirectionAgvToMes, request?.position, request?.position,
+ null, request?.requestId, request?.position, request?.type, request?.materialCode, request?.agvNo,
+ "", requestText, "", 2, ex.Message);
+ SaveMesLog("LGAGV_StatusUpdate", ex.Message, MesLogType.ERROR);
+ }
+
+ string responseText = JsonConvert.SerializeObject(result);
+ SaveLog_Interface_Response(responseText, aid);
+ return responseText;
+ }
+
+ private static void WriteOp10PlcStatus(LGAGV_StatusUpdateRequest request)
+ {
+ if (request == null || string.IsNullOrWhiteSpace(request.position))
+ {
+ return;
+ }
+
+ string position = request.position.Trim();
+ int tagTypeCodeID = 0;
+ if (request.type == 1 && position.StartsWith("OP10-1-", StringComparison.OrdinalIgnoreCase))
+ {
+ tagTypeCodeID = 28;
+ }
+ else if (request.type == 1 && position.StartsWith("OP10-2-", StringComparison.OrdinalIgnoreCase))
+ {
+ tagTypeCodeID = 29;
+ }
+ else if (request.type == 2 && position.StartsWith("OP10-2-", StringComparison.OrdinalIgnoreCase))
+ {
+ tagTypeCodeID = 30;
+ }
+
+ if (tagTypeCodeID == 0)
+ {
+ return;
+ }
+
+ bool writeResult = MIS_BASE.WritePLC(tagTypeCodeID, "AC1101", true);
+ string actionDesc = request.type == 1 ? "到达" : "离开";
+ if (writeResult)
+ {
+ SaveMesLog("LGAGV_StatusUpdate", $"OP10 AGV{actionDesc}回写PLC成功:点位={position}, TagTypeCodeID={tagTypeCodeID}", MesLogType.INFO);
+ }
+ else
+ {
+ SaveMesLog("LGAGV_StatusUpdate", $"OP10 AGV{actionDesc}回写PLC失败:点位={position}, TagTypeCodeID={tagTypeCodeID}", MesLogType.ERROR);
+ }
+ }
+
+ ///
+ /// 处理临工AGV任务结果回调。
+ ///
+ public static string ProcessTaskResult(string url, [FromBody] JObject jobj)
+ {
+ var result = LGAGV_Response.Success();
+ string requestText = jobj?.ToString() ?? "";
+ SaveLog_Interface_Request(url, 2, requestText, out int aid);
+
+ LGAGV_TaskResultRequest request = null;
+ try
+ {
+ if (jobj == null)
+ {
+ throw new Exception("请求参数不能为空");
+ }
+
+ request = JsonConvert.DeserializeObject(requestText);
+ if (request == null)
+ {
+ throw new Exception("请求参数格式不正确");
+ }
+ if (string.IsNullOrWhiteSpace(request.requestTaskId))
+ {
+ throw new Exception("任务ID(requestTaskId)不能为空");
+ }
+
+ bool isSuccess = IsAgvSuccess(request.status);
+ var param = new SqlParameter[]
+ {
+ new SqlParameter("@requestTaskId", request.requestTaskId ?? ""),
+ new SqlParameter("@status", request.status ?? ""),
+ new SqlParameter("@agvNo", request.agvNo ?? ""),
+ new SqlParameter("@message", request.message ?? ""),
+ new SqlParameter("@materialCode", request.materialCode ?? "")
+ };
+ ExecuteResultProcedure("LG_AGV_库位Moby_任务结果", ref param);
+
+ SaveInterfaceRecord("TaskResult", DirectionAgvToMes, null, null,
+ request.requestTaskId, null, null, null, request.materialCode, request.agvNo, request.status,
+ requestText, "", isSuccess ? 1 : 2, request.message ?? "");
+
+ SaveMesLog("LGAGV_TaskResult", $"临工AGV任务结果:requestTaskId={request.requestTaskId}, status={request.status}, AGV={request.agvNo}, message={request.message}", MesLogType.INFO);
+ }
+ catch (Exception ex)
+ {
+ result = LGAGV_Response.Fail(ex.Message);
+ SaveInterfaceRecord("TaskResult", DirectionAgvToMes, null, null,
+ request?.requestTaskId, null, null, null, request?.materialCode, request?.agvNo, request?.status,
+ requestText, "", 2, ex.Message);
+ SaveMesLog("LGAGV_TaskResult", ex.Message, MesLogType.ERROR);
+ }
+
+ string responseText = JsonConvert.SerializeObject(result);
+ SaveLog_Interface_Response(responseText, aid);
+ return responseText;
+ }
+
+ public static void SaveInterfaceRecord(string interfaceName, string direction, string positionCode, string workStation,
+ string requestTaskId, string requestId, string position, int? type, string materialCode, string agvNo, string status,
+ string requestText, string responseText, int isSuccess, string errorMessage)
+ {
+ var param = new SqlParameter[]
+ {
+ new SqlParameter("@库位号", positionCode ?? ""),
+ new SqlParameter("@工位号", workStation ?? ""),
+ new SqlParameter("@接口名称", interfaceName ?? ""),
+ new SqlParameter("@接口方向", direction ?? ""),
+ new SqlParameter("@requestTaskId", requestTaskId ?? ""),
+ new SqlParameter("@requestId", requestId ?? ""),
+ new SqlParameter("@position", position ?? ""),
+ new SqlParameter("@type", type.HasValue ? (object)type.Value : DBNull.Value),
+ new SqlParameter("@materialCode", materialCode ?? ""),
+ new SqlParameter("@agvNo", agvNo ?? ""),
+ new SqlParameter("@status", status ?? ""),
+ new SqlParameter("@message", errorMessage ?? ""),
+ new SqlParameter("@请求报文", requestText ?? ""),
+ new SqlParameter("@响应报文", responseText ?? ""),
+ new SqlParameter("@是否成功", isSuccess),
+ new SqlParameter("@错误信息", errorMessage ?? "")
+ };
+ DataLinkMesWork2.DataAccess2.ExecuteStoredProcedure("LG_AGV_接口记录_增加", ref param, out string errMessage);
+ if (!string.IsNullOrEmpty(errMessage))
+ {
+ SaveMesLog("LGAGV_InterfaceRecord", errMessage, MesLogType.ERROR);
+ }
+ }
+
+ public static bool IsAgvSuccess(string codeOrStatus)
+ {
+ string value = (codeOrStatus ?? "").Trim().ToLower();
+ return value == "0" || value == "200" || value == "true" || value == "success" || value == "成功";
+ }
+
+ private static LGAGV_CallResult PostToAgv(string interfaceName, string path, object request,
+ string requestTaskId, string requestId, string position, int? type, string materialCode, string agvNo)
+ {
+ string baseUrl = ConfigurationManager.AppSettings["LGAGV_BaseUrl"];
+ if (string.IsNullOrWhiteSpace(baseUrl))
+ {
+ throw new Exception("未配置 LGAGV_BaseUrl");
+ }
+
+ string interfaceUrl = baseUrl.TrimEnd('/') + path;
+ string requestText = JsonConvert.SerializeObject(request);
+ SaveLog_Interface_Request("LGAGV_" + interfaceName, 1, requestText, out int aid);
+
+ string responseText = Post.Post_Auto(interfaceUrl, requestText);
+ SaveLog_Interface_Response(responseText, aid);
+
+ JObject response = ParseResponse(responseText);
+ string codeText = response["code"]?.ToString() ?? response["result"]?.ToString() ?? "";
+ string message = response["desc"]?.ToString() ?? response["message"]?.ToString() ?? response["msg"]?.ToString() ?? "";
+ bool success = IsAgvSuccess(codeText);
+ if (response["success"] != null)
+ {
+ if (bool.TryParse(response["success"].ToString(), out bool successValue))
+ {
+ success = successValue;
+ }
+ }
+
+ SaveInterfaceRecord(interfaceName, DirectionMesToAgv, position, position, requestTaskId, requestId, position,
+ type, materialCode, agvNo, codeText, requestText, responseText, success ? 1 : 2, success ? "" : message);
+
+ if (success)
+ {
+ SaveMesLog("LGAGV_" + interfaceName, $"临工AGV接口调用成功:{interfaceName}, position={position}, requestTaskId={requestTaskId}", MesLogType.INFO);
+ }
+ else
+ {
+ SaveMesLog("LGAGV_" + interfaceName, $"临工AGV接口调用失败:{interfaceName}, code={codeText}, message={message}", MesLogType.ERROR);
+ }
+
+ return new LGAGV_CallResult
+ {
+ Success = success,
+ Code = ToInt(codeText, success ? 0 : 500),
+ Message = message,
+ InterfaceUrl = interfaceUrl,
+ RequestText = requestText,
+ ResponseText = responseText,
+ Response = response
+ };
+ }
+
+ private static JObject ParseResponse(string responseText)
+ {
+ if (string.IsNullOrWhiteSpace(responseText))
+ {
+ return new JObject
+ {
+ { "code", 500 },
+ { "message", "AGV返回空响应" },
+ { "success", false }
+ };
+ }
+
+ try
+ {
+ return JObject.Parse(responseText);
+ }
+ catch (Exception ex)
+ {
+ return new JObject
+ {
+ { "code", 500 },
+ { "message", "AGV返回非JSON响应:" + ex.Message },
+ { "raw", responseText },
+ { "success", false }
+ };
+ }
+ }
+
+ private static int ToInt(string value, int defaultValue)
+ {
+ if (int.TryParse(value, out int intValue))
+ {
+ return intValue;
+ }
+ return defaultValue;
+ }
+
+ private static void ExecuteResultProcedure(string procedureName, ref SqlParameter[] param)
+ {
+ DataLinkMesWork2.DataAccess2.ExecuteStoredProcedure(procedureName, ref param, out DataTable dt, out string errMessage);
+ if (!string.IsNullOrEmpty(errMessage))
+ {
+ throw new Exception(errMessage);
+ }
+ if (dt != null && dt.Rows.Count > 0 && dt.Columns.Contains("result") && dt.Rows[0]["result"].ToString() != "1")
+ {
+ string msg = dt.Columns.Contains("msg") ? dt.Rows[0]["msg"].ToString() : "存储过程执行失败";
+ throw new Exception(msg);
+ }
+ }
+ }
+}
diff --git a/Interface_WebAPI/SlMesDbIterface/WebAPI/AGV/LGAGV_Models.cs b/Interface_WebAPI/SlMesDbIterface/WebAPI/AGV/LGAGV_Models.cs
new file mode 100644
index 0000000..db77b26
--- /dev/null
+++ b/Interface_WebAPI/SlMesDbIterface/WebAPI/AGV/LGAGV_Models.cs
@@ -0,0 +1,106 @@
+using Newtonsoft.Json.Linq;
+
+namespace MisDataSaveDate
+{
+ ///
+ /// 临工AGV两点任务请求。
+ ///
+ public class LGAGV_AddTaskRequest
+ {
+ public string requestTaskId { get; set; }
+ public string startPosition { get; set; }
+ public string endPosition { get; set; }
+ public string materialCode { get; set; }
+ }
+
+ ///
+ /// 临工AGV三点任务请求。
+ ///
+ public class LGAGV_AddTask2Request
+ {
+ public string requestTaskId { get; set; }
+ public string startPosition { get; set; }
+ public string midPosition { get; set; }
+ public string endPosition { get; set; }
+ public string materialCode { get; set; }
+ }
+
+ ///
+ /// 临工AGV放行请求。
+ ///
+ public class LGAGV_AllowLeaveRequest
+ {
+ public string requestId { get; set; }
+ public string position { get; set; }
+ public string agvNo { get; set; }
+ public int allowLeave { get; set; } = 1;
+ }
+
+ ///
+ /// 临工AGV到达/离开回调。
+ ///
+ public class LGAGV_StatusUpdateRequest
+ {
+ public string requestId { get; set; }
+ public string position { get; set; }
+ public int type { get; set; }
+ public string materialCode { get; set; }
+ public string agvNo { get; set; }
+ }
+
+ ///
+ /// 临工AGV任务执行结果回调。
+ ///
+ public class LGAGV_TaskResultRequest
+ {
+ public string requestTaskId { get; set; }
+ public string status { get; set; }
+ public string agvNo { get; set; }
+ public string message { get; set; }
+ public string materialCode { get; set; }
+ }
+
+ ///
+ /// 返回给临工AGV的通用响应。
+ ///
+ public class LGAGV_Response
+ {
+ public int code { get; set; }
+ public string desc { get; set; }
+ public bool success { get; set; }
+
+ public static LGAGV_Response Success(string desc = "成功")
+ {
+ return new LGAGV_Response
+ {
+ code = 0,
+ desc = desc,
+ success = true
+ };
+ }
+
+ public static LGAGV_Response Fail(string desc)
+ {
+ return new LGAGV_Response
+ {
+ code = 1,
+ desc = desc,
+ success = false
+ };
+ }
+ }
+
+ ///
+ /// MES调用临工AGV接口后的内部结果。
+ ///
+ public class LGAGV_CallResult
+ {
+ public bool Success { get; set; }
+ public int Code { get; set; }
+ public string Message { get; set; }
+ public string InterfaceUrl { get; set; }
+ public string RequestText { get; set; }
+ public string ResponseText { get; set; }
+ public JObject Response { get; set; }
+ }
+}
diff --git a/Interface_WebAPI/SlMesDbIterface/WebAPI/AGV/TCSAGV_BusinessLogic.cs b/Interface_WebAPI/SlMesDbIterface/WebAPI/AGV/TCSAGV_BusinessLogic.cs
new file mode 100644
index 0000000..6530d06
--- /dev/null
+++ b/Interface_WebAPI/SlMesDbIterface/WebAPI/AGV/TCSAGV_BusinessLogic.cs
@@ -0,0 +1,836 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.SqlClient;
+using System.Net.Http;
+using System.Text;
+using System.Web;
+using System.Web.Http;
+using MesServerWork;
+using MisDataSaveDate.Helper;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using static MisDataSaveDate.Helper.ApiLogHelper;
+
+namespace MisDataSaveDate
+{
+ ///
+ /// TCS插拔任务业务处理。PLC自动任务与Web人工任务共用此入口。
+ ///
+ public class TCSAGV_BusinessLogic
+ {
+ private const string TaskTypeCombined = "nav_action_program";
+ private const string TaskTypeProgram = "program";
+
+ public static TCSAGV_CallResult DispatchStationTask(string stationCode, string actionType,
+ string portCombination, string source, string operatorCode, string parentExternalCode, bool notifyPlc)
+ {
+ TCSAGV_Config config = ReadConfig(true);
+ TCSAGV_PointConfig point = ReadPointConfig(stationCode);
+ string normalizedAction = NormalizeAction(actionType);
+ string productModel = "";
+ string combination;
+
+ if (string.IsNullOrWhiteSpace(portCombination))
+ {
+ productModel = ReadCurrentProductModel(point.StationCode);
+ combination = ReadModelActionCombination(point.LineCode, productModel, normalizedAction);
+ }
+ else
+ {
+ combination = NormalizePortCombination(portCombination);
+ }
+
+ JArray parameters = new JArray
+ {
+ new JObject { { "workStation", point.WorkStation } }
+ };
+ if (combination.Contains("XSD"))
+ {
+ parameters.Add(new JObject
+ {
+ { "programName", normalizedAction == "插" ? point.InsertXsdProgram : point.PullXsdProgram }
+ });
+ }
+ if (combination.Contains("XSE"))
+ {
+ parameters.Add(new JObject
+ {
+ { "programName", normalizedAction == "插" ? point.InsertXseProgram : point.PullXseProgram }
+ });
+ }
+
+ string externalCode = CreateExternalCode(normalizedAction == "插" ? "PLUG" : "PULL");
+ return CreateAndSendTask(config, point, externalCode, parentExternalCode, normalizedAction, "",
+ productModel, combination, TaskTypeCombined, parameters.ToString(Formatting.None), source,
+ operatorCode, notifyPlc);
+ }
+
+ public static TCSAGV_CallResult DispatchSingleStep(string stationCode, string programName,
+ string operatorCode, string parentExternalCode)
+ {
+ TCSAGV_Config config = ReadConfig(true);
+ TCSAGV_PointConfig point = ReadPointConfig(stationCode);
+ EnsureRobotAtPoint(config, point);
+ string normalizedProgram = (programName ?? "").Trim();
+ if (!IsConfiguredProgram(point, normalizedProgram))
+ {
+ throw new Exception("所选programName不属于该工位配置");
+ }
+
+ string action = normalizedProgram.Contains("拔") ? "拔-单步" : "插-单步";
+ JArray parameters = new JArray
+ {
+ new JObject { { "programName", normalizedProgram } }
+ };
+ return CreateAndSendTask(config, point, CreateExternalCode("STEP"), parentExternalCode,
+ action, normalizedProgram, "", normalizedProgram.Contains("XSD") ? "XSD" : "XSE",
+ TaskTypeProgram, parameters.ToString(Formatting.None),
+ "MANUAL", operatorCode, false);
+ }
+
+ public static TCSAGV_CallResult RetryTask(string externalCode, string operatorCode)
+ {
+ DataRow original = ReadTask(externalCode);
+ if (!IsFailureStatus(ToInt(original["任务状态"], -1)))
+ {
+ throw new Exception("仅任务错误状态允许整单重发");
+ }
+ if (ToBool(original["处理状态"]))
+ {
+ throw new Exception("失败任务已处理,不能再次重发");
+ }
+ if (!ToBool(original["已复位"]))
+ {
+ throw new Exception("请先复位移动机器人再重发任务");
+ }
+
+ TCSAGV_Config config = ReadConfig(true);
+ TCSAGV_PointConfig point = ReadPointConfig(Convert.ToString(original["MES工位号"]));
+ string action = Convert.ToString(original["任务动作"]);
+ string programName = Convert.ToString(original["ProgramName"]);
+ string productModel = Convert.ToString(original["产品型号"]);
+ string combination = Convert.ToString(original["动作组合"]);
+ string taskType = Convert.ToString(original["任务类型"]);
+ string param = Convert.ToString(original["参数"]);
+
+ return CreateAndSendTask(config, point, CreateExternalCode("RETRY"), externalCode,
+ action, programName, productModel, combination, taskType, param, "RETRY", operatorCode, false);
+ }
+
+ public static TCSAGV_CallResult CancelTask(string externalCode, string operatorCode)
+ {
+ if (string.IsNullOrWhiteSpace(externalCode))
+ {
+ throw new Exception("外部任务编号不能为空");
+ }
+
+ TCSAGV_Config config = ReadConfig(true);
+ TCSAGV_CallResult result = GetFromTcs(config, "cancelExternalBusinessTask",
+ "/api/cancelExternalBusinessTask", new Dictionary { { "externalCode", externalCode } });
+ if (result.Success)
+ {
+ SetLocalTaskStatus(externalCode, 3, "已取消", "");
+ }
+ SaveOperation(externalCode, "", "取消任务", operatorCode, result.RequestText,
+ result.ResponseText, result.Success, result.Message);
+ return result;
+ }
+
+ public static TCSAGV_CallResult StopPendingTasks(string externalCode, string operatorCode)
+ {
+ TCSAGV_Config config = ReadConfig(true);
+ TCSAGV_CallResult result = GetFromTcs(config, "stopExternalBusinessTask",
+ "/api/stopExternalBusinessTask", null);
+ if (result.Success && !string.IsNullOrWhiteSpace(externalCode))
+ {
+ DataRow task = ReadTask(externalCode);
+ if (ToInt(task["任务状态"], -1) == 0)
+ {
+ SetLocalTaskStatus(externalCode, 3, "已停止", "停止全部未开始任务");
+ }
+ }
+ SaveOperation(externalCode, "", "停止未开始任务", operatorCode, result.RequestText,
+ result.ResponseText, result.Success, result.Message);
+ return result;
+ }
+
+ public static TCSAGV_CallResult ResetRobot(string externalCode, string ip, string operatorCode)
+ {
+ TCSAGV_Config config = ReadConfig(true);
+ string robotIp = string.IsNullOrWhiteSpace(ip) ? config.McrIp : ip.Trim();
+ if (string.IsNullOrWhiteSpace(robotIp))
+ {
+ throw new Exception("未配置AGV IP");
+ }
+
+ TCSAGV_CallResult result = GetFromTcs(config, "reset", "/api/reset",
+ new Dictionary { { "ip", robotIp } });
+ if (result.Success && !string.IsNullOrWhiteSpace(externalCode))
+ {
+ try
+ {
+ SqlParameter[] parameters =
+ {
+ new SqlParameter("@外部任务编号", externalCode.Trim())
+ };
+ EnsureBusinessSuccess(ExecuteProcedure("XD_TCS_AGV_任务_复位标记", parameters));
+ }
+ catch (Exception ex)
+ {
+ SaveOperation(externalCode, "", "移动机器人复位", operatorCode, result.RequestText,
+ result.ResponseText, false, "TCS复位成功,但MES复位标记失败:" + ex.Message);
+ throw;
+ }
+ }
+ SaveOperation(externalCode, "", "移动机器人复位", operatorCode, result.RequestText,
+ result.ResponseText, result.Success, result.Message);
+ return result;
+ }
+
+ public static TCSAGV_CallResult StopNow(string externalCode, string ip, string operatorCode)
+ {
+ TCSAGV_Config config = ReadConfig(true);
+ string robotIp = string.IsNullOrWhiteSpace(ip) ? config.McrIp : ip.Trim();
+ if (string.IsNullOrWhiteSpace(robotIp))
+ {
+ throw new Exception("未配置AGV IP");
+ }
+
+ TCSAGV_CallResult result = GetFromTcs(config, "stopNow", "/api/stopNow",
+ new Dictionary { { "ip", robotIp } });
+ if (result.Success && !string.IsNullOrWhiteSpace(externalCode))
+ {
+ SetLocalTaskStatus(externalCode, 4, "任务错误", "人工紧急停止");
+ }
+ SaveOperation(externalCode, "", "紧急停止", operatorCode, result.RequestText,
+ result.ResponseText, result.Success, result.Message);
+ return result;
+ }
+
+ public static TCSAGV_CallResult SyncTaskStatus(string externalCode, string operatorCode)
+ {
+ TCSAGV_Config config = ReadConfig(true);
+ TCSAGV_CallResult result = GetFromTcs(config, "getExternalBusinessTaskStatus",
+ "/api/getExternalBusinessTaskStatus", new Dictionary { { "externalCode", externalCode } });
+ if (result.Success)
+ {
+ JObject task = FirstResultObject(result.Response);
+ if (task != null && task["status"] != null)
+ {
+ int status = ToInt(task["status"], -1);
+ if (status >= 0 && status <= 5)
+ {
+ UpdateTaskStatus(externalCode, status,
+ Convert.ToString(task["ip"] ?? ""), Convert.ToString(task["type"] ?? ""),
+ Convert.ToString(task["beginTime"] ?? ""), Convert.ToString(task["endTime"] ?? ""),
+ Convert.ToString(task["remark"] ?? ""));
+ }
+ }
+ }
+ SaveOperation(externalCode, "", "同步任务状态", operatorCode, result.RequestText,
+ result.ResponseText, result.Success, result.Message);
+ return result;
+ }
+
+ public static TCSAGV_CallResult QueryRobotInfo()
+ {
+ TCSAGV_Config config = ReadConfig(true);
+ return QueryRobotInfo(config);
+ }
+
+ private static TCSAGV_CallResult QueryRobotInfo(TCSAGV_Config config)
+ {
+ Dictionary query = new Dictionary
+ {
+ { "mcrGroup", config.McrGroup }
+ };
+ if (!string.IsNullOrWhiteSpace(config.McrIp))
+ {
+ query.Add("mcrIp", config.McrIp);
+ }
+ return GetFromTcs(config, "getMcrInfo", "/api/getMcrInfo", query);
+ }
+
+ public static void HandleFailure(string externalCode, string operatorCode, string remark, bool notifyPlc)
+ {
+ DataRow task = ReadTask(externalCode);
+ if (!IsFailureStatus(ToInt(task["任务状态"], -1)))
+ {
+ throw new Exception("仅任务错误状态允许人工处理");
+ }
+ if (!ToBool(task["已复位"]))
+ {
+ throw new Exception("请先复位移动机器人再完成人工处理");
+ }
+ if (string.IsNullOrWhiteSpace(remark))
+ {
+ throw new Exception("处理说明不能为空");
+ }
+
+ if (notifyPlc)
+ {
+ WritePlcCompletion(task);
+ }
+
+ SqlParameter[] parameters =
+ {
+ new SqlParameter("@外部任务编号", externalCode ?? ""),
+ new SqlParameter("@处理人", operatorCode ?? ""),
+ new SqlParameter("@处理说明", remark ?? "")
+ };
+ EnsureBusinessSuccess(ExecuteProcedure("XD_TCS_AGV_任务_人工处理", parameters));
+ SaveOperation(externalCode, Convert.ToString(task["MES工位号"]), "失败任务人工处理",
+ operatorCode, "", "", true, remark);
+ }
+
+ public static string ProcessTaskStatus(string url, [FromBody] JObject jobj)
+ {
+ string requestText = jobj == null ? "" : jobj.ToString(Formatting.None);
+ SaveLog_Interface_Request(url, 2, requestText, out int aid);
+ TCSAGV_ApiResponse response;
+ try
+ {
+ if (jobj == null)
+ {
+ throw new Exception("请求参数不能为空");
+ }
+ TCSAGV_TaskStatusPushRequest request = jobj.ToObject();
+ if (request == null || string.IsNullOrWhiteSpace(request.externalCode))
+ {
+ throw new Exception("外部任务编号不能为空");
+ }
+ int status = ToInt(request.status, -1);
+ if (status < 0 || status > 5)
+ {
+ throw new Exception("任务状态必须为0至5");
+ }
+
+ UpdateTaskStatus(request.externalCode, status, request.ip, request.type,
+ request.beginTime, request.endTime, request.remark);
+ response = TCSAGV_ApiResponse.Ok("接收成功", "接收成功");
+ }
+ catch (Exception ex)
+ {
+ response = TCSAGV_ApiResponse.Fail(ex.Message);
+ SaveMesLog("TCS任务状态推送", ex.Message, MesLogType.ERROR);
+ }
+
+ string responseText = JsonConvert.SerializeObject(response);
+ SaveLog_Interface_Response(responseText, aid);
+ return responseText;
+ }
+
+ public static string ProcessPositionPush(string url, [FromBody] JObject jobj)
+ {
+ string requestText = jobj == null ? "" : jobj.ToString(Formatting.None);
+ SaveLog_Interface_Request(url, 2, requestText, out int aid);
+ TCSAGV_ApiResponse response;
+ try
+ {
+ if (jobj == null)
+ {
+ throw new Exception("请求参数不能为空");
+ }
+ TCSAGV_PositionPushRequest request = jobj.ToObject();
+ if (request == null || string.IsNullOrWhiteSpace(request.ip))
+ {
+ throw new Exception("机器人IP不能为空");
+ }
+
+ SqlParameter[] parameters =
+ {
+ new SqlParameter("@机器人IP", request.ip ?? ""),
+ new SqlParameter("@当前站点", request.currentPos ?? ""),
+ new SqlParameter("@目标站点", request.objectivePos ?? ""),
+ new SqlParameter("@原始报文", requestText)
+ };
+ ExecuteProcedure("XD_TCS_AGV_位置_更新", parameters);
+ response = TCSAGV_ApiResponse.Ok("接收成功", "接收成功");
+ }
+ catch (Exception ex)
+ {
+ response = TCSAGV_ApiResponse.Fail(ex.Message);
+ SaveMesLog("TCS位置推送", ex.Message, MesLogType.ERROR);
+ }
+
+ string responseText = JsonConvert.SerializeObject(response);
+ SaveLog_Interface_Response(responseText, aid);
+ return responseText;
+ }
+
+ private static TCSAGV_CallResult CreateAndSendTask(TCSAGV_Config config, TCSAGV_PointConfig point,
+ string externalCode, string parentExternalCode, string action, string programName,
+ string productModel, string combination, string taskType, string param, string source,
+ string operatorCode, bool notifyPlc)
+ {
+ TCSAGV_TaskRequest request = new TCSAGV_TaskRequest
+ {
+ externalCode = externalCode,
+ type = taskType,
+ priority = config.DefaultPriority,
+ param = param,
+ mcrgroup = config.McrGroup,
+ mcrip = config.McrIp,
+ operatoruserid = config.OperatorUserId
+ };
+
+ SqlParameter[] createParameters =
+ {
+ new SqlParameter("@外部任务编号", externalCode),
+ new SqlParameter("@原任务编号", parentExternalCode ?? ""),
+ new SqlParameter("@MES工位号", point.StationCode),
+ new SqlParameter("@任务动作", action),
+ new SqlParameter("@产品型号", productModel ?? ""),
+ new SqlParameter("@动作组合", combination ?? ""),
+ new SqlParameter("@ProgramName", programName ?? ""),
+ new SqlParameter("@任务类型", taskType),
+ new SqlParameter("@参数", param),
+ new SqlParameter("@优先级", config.DefaultPriority),
+ new SqlParameter("@机器人组", config.McrGroup ?? ""),
+ new SqlParameter("@机器人IP", config.McrIp ?? ""),
+ new SqlParameter("@来源", source ?? ""),
+ new SqlParameter("@操作人", operatorCode ?? ""),
+ new SqlParameter("@是否通知PLC", notifyPlc)
+ };
+ EnsureBusinessSuccess(ExecuteProcedure("XD_TCS_AGV_任务_创建", createParameters));
+
+ TCSAGV_CallResult result;
+ try
+ {
+ result = PostToTcs(config, "createExternalBusinessTask", "/api/createExternalBusinessTask", request);
+ }
+ catch (Exception ex)
+ {
+ UpdateDispatchResult(externalCode, JsonConvert.SerializeObject(request), "", false, ex.Message);
+ SaveOperation(externalCode, point.StationCode, "下发" + action + "任务", operatorCode,
+ JsonConvert.SerializeObject(request), "", false, ex.Message);
+ throw;
+ }
+
+ UpdateDispatchResult(externalCode, result.RequestText, result.ResponseText, result.Success,
+ result.Success ? "" : result.Message);
+ SaveOperation(externalCode, point.StationCode, "下发" + action + "任务", operatorCode,
+ result.RequestText, result.ResponseText, result.Success, result.Message);
+ result.ExternalCode = externalCode;
+ return result;
+ }
+
+ private static void UpdateTaskStatus(string externalCode, int status, string ip, string type,
+ string beginTime, string endTime, string remark)
+ {
+ SqlParameter[] parameters =
+ {
+ new SqlParameter("@外部任务编号", externalCode ?? ""),
+ new SqlParameter("@任务状态", status),
+ new SqlParameter("@机器人IP", ip ?? ""),
+ new SqlParameter("@任务类型", type ?? ""),
+ new SqlParameter("@开始时间", beginTime ?? ""),
+ new SqlParameter("@结束时间", endTime ?? ""),
+ new SqlParameter("@备注", remark ?? "")
+ };
+ DataTable result = ExecuteProcedure("XD_TCS_AGV_任务_状态更新", parameters);
+ EnsureBusinessSuccess(result);
+
+ if (status == 2 && result.Rows.Count > 0 && ToBool(result.Rows[0]["是否通知PLC"])
+ && !ToBool(result.Rows[0]["PLC已通知"]))
+ {
+ DataRow task = ReadTask(externalCode);
+ WritePlcCompletion(task);
+ SqlParameter[] notified = { new SqlParameter("@外部任务编号", externalCode) };
+ ExecuteProcedure("XD_TCS_AGV_任务_PLC通知完成", notified);
+ }
+ }
+
+ private static bool IsFailureStatus(int status)
+ {
+ return status == 4 || status == 5;
+ }
+
+ private static void WritePlcCompletion(DataRow task)
+ {
+ string stationCode = Convert.ToString(task["MES工位号"]);
+ string action = Convert.ToString(task["任务动作"]);
+ int tagCode = action.StartsWith("插", StringComparison.Ordinal) ? 400
+ : action.StartsWith("拔", StringComparison.Ordinal) ? 401 : 0;
+ if (tagCode == 0)
+ {
+ throw new Exception("任务动作无法确定PLC完成码");
+ }
+ if (!MIS_BASE.WritePLC(tagCode, stationCode, true))
+ {
+ throw new Exception("任务完成但PLC完成信号写入失败");
+ }
+ SaveMesLog(stationCode, "TCS任务完成,已写入PLC功能码" + tagCode, MesLogType.INFO);
+ }
+
+ private static TCSAGV_Config ReadConfig(bool requireEnabled)
+ {
+ DataTable table = ExecuteProcedure("XD_TCS_AGV_配置_获取", new SqlParameter[0]);
+ if (table.Rows.Count == 0)
+ {
+ throw new Exception("未配置TCS参数");
+ }
+ DataRow row = table.Rows[0];
+ TCSAGV_Config config = new TCSAGV_Config
+ {
+ BaseUrl = Convert.ToString(row["TCS_BaseUrl"]),
+ Enabled = Convert.ToString(row["TCS_Enabled"]) == "1",
+ McrGroup = Convert.ToString(row["McrGroup"]),
+ McrIp = Convert.ToString(row["McrIp"]),
+ OperatorUserId = Convert.ToString(row["TCS_OperatorUserId"]),
+ DefaultPriority = ToInt(row["DefaultPriority"], 1),
+ HttpTimeoutSeconds = ToInt(row["HttpTimeoutSeconds"], 15)
+ };
+ if (string.IsNullOrWhiteSpace(config.BaseUrl))
+ {
+ throw new Exception("未配置TCS服务地址");
+ }
+ if (string.IsNullOrWhiteSpace(config.OperatorUserId))
+ {
+ throw new Exception("未配置TCS任务状态推送标识");
+ }
+ if (requireEnabled && !config.Enabled)
+ {
+ throw new Exception("TCS真实下发开关未启用,请先确认现场地址和AGV IP");
+ }
+ return config;
+ }
+
+ private static TCSAGV_PointConfig ReadPointConfig(string stationCode)
+ {
+ if (string.IsNullOrWhiteSpace(stationCode))
+ {
+ throw new Exception("MES工位号不能为空");
+ }
+ SqlParameter[] parameters = { new SqlParameter("@MES工位号", stationCode.Trim()) };
+ DataTable table = ExecuteProcedure("XD_TCS_AGV_点位配置_查询", parameters);
+ if (table.Rows.Count == 0)
+ {
+ throw new Exception("MES工位未配置TCS站点和programName");
+ }
+ DataRow row = table.Rows[0];
+ return new TCSAGV_PointConfig
+ {
+ LineCode = Convert.ToString(row["产线代码"]),
+ StationCode = Convert.ToString(row["MES工位号"]),
+ PositionName = Convert.ToString(row["现场位置"]),
+ WorkStation = Convert.ToString(row["WorkStation"]),
+ InsertXsdProgram = Convert.ToString(row["插XSDProgramName"]),
+ InsertXseProgram = Convert.ToString(row["插XSEProgramName"]),
+ PullXsdProgram = Convert.ToString(row["拔XSDProgramName"]),
+ PullXseProgram = Convert.ToString(row["拔XSEProgramName"])
+ };
+ }
+
+ private static string ReadCurrentProductModel(string stationCode)
+ {
+ SqlParameter[] parameters = { new SqlParameter("@MES工位号", stationCode) };
+ DataTable table = ExecuteProcedure("XD_TCS_AGV_库位产品_查询", parameters);
+ string productModel = table.Rows.Count == 0 ? "" : Convert.ToString(table.Rows[0]["产品型号"]);
+ if (string.IsNullOrWhiteSpace(productModel))
+ {
+ throw new Exception("工位" + stationCode + "未获取到当前产品型号");
+ }
+ return productModel.Trim();
+ }
+
+ private static string ReadModelActionCombination(string lineCode, string productModel, string action)
+ {
+ SqlParameter[] parameters =
+ {
+ new SqlParameter("@产线代码", lineCode),
+ new SqlParameter("@产品型号", productModel),
+ new SqlParameter("@任务动作", action)
+ };
+ DataTable table = ExecuteProcedure("XD_TCS_AGV_型号动作映射_查询", parameters);
+ if (table.Rows.Count == 0)
+ {
+ throw new Exception("产品型号" + productModel + "未配置" + action + "动作组合");
+ }
+ return Convert.ToString(table.Rows[0]["动作组合"]);
+ }
+
+ private static DataRow ReadTask(string externalCode)
+ {
+ if (string.IsNullOrWhiteSpace(externalCode))
+ {
+ throw new Exception("外部任务编号不能为空");
+ }
+ SqlParameter[] parameters = { new SqlParameter("@外部任务编号", externalCode.Trim()) };
+ DataTable table = ExecuteProcedure("XD_TCS_AGV_任务_获取", parameters);
+ if (table.Rows.Count == 0)
+ {
+ throw new Exception("外部任务不存在");
+ }
+ return table.Rows[0];
+ }
+
+ private static void EnsureRobotAtPoint(TCSAGV_Config config, TCSAGV_PointConfig point)
+ {
+ TCSAGV_CallResult result = QueryRobotInfo(config);
+ if (!result.Success)
+ {
+ throw new Exception("查询AGV实时位置失败:" + result.Message);
+ }
+ JObject robot = FirstResultObject(result.Response);
+ if (robot == null)
+ {
+ throw new Exception("未查询到AGV状态,不能执行单步任务");
+ }
+ if (ToInt(robot["status"], -1) == 4)
+ {
+ throw new Exception("AGV当前未连接,不能执行单步任务");
+ }
+ string currentPosition = Convert.ToString(robot["currentPos"]);
+ if (string.IsNullOrWhiteSpace(currentPosition))
+ {
+ throw new Exception("未获取到AGV当前位置,不能执行单步任务");
+ }
+ if (!string.Equals(currentPosition, point.WorkStation, StringComparison.OrdinalIgnoreCase))
+ {
+ throw new Exception("AGV当前位置" + currentPosition + "与所选站点" + point.WorkStation + "不一致");
+ }
+ }
+
+ private static void UpdateDispatchResult(string externalCode, string requestText, string responseText,
+ bool success, string errorMessage)
+ {
+ SqlParameter[] parameters =
+ {
+ new SqlParameter("@外部任务编号", externalCode ?? ""),
+ new SqlParameter("@请求报文", requestText ?? ""),
+ new SqlParameter("@响应报文", responseText ?? ""),
+ new SqlParameter("@是否成功", success),
+ new SqlParameter("@错误信息", errorMessage ?? "")
+ };
+ ExecuteProcedure("XD_TCS_AGV_任务_下发结果更新", parameters);
+ }
+
+ private static void SetLocalTaskStatus(string externalCode, int status, string statusText, string errorMessage)
+ {
+ SqlParameter[] parameters =
+ {
+ new SqlParameter("@外部任务编号", externalCode ?? ""),
+ new SqlParameter("@任务状态", status),
+ new SqlParameter("@状态说明", statusText ?? ""),
+ new SqlParameter("@错误信息", errorMessage ?? "")
+ };
+ ExecuteProcedure("XD_TCS_AGV_任务_本地状态设置", parameters);
+ }
+
+ private static void SaveOperation(string externalCode, string stationCode, string operationType,
+ string operatorCode, string requestText, string responseText, bool success, string message)
+ {
+ try
+ {
+ SqlParameter[] parameters =
+ {
+ new SqlParameter("@外部任务编号", externalCode ?? ""),
+ new SqlParameter("@MES工位号", stationCode ?? ""),
+ new SqlParameter("@操作类型", operationType ?? ""),
+ new SqlParameter("@操作人", operatorCode ?? ""),
+ new SqlParameter("@请求报文", requestText ?? ""),
+ new SqlParameter("@响应报文", responseText ?? ""),
+ new SqlParameter("@是否成功", success),
+ new SqlParameter("@操作结果", message ?? "")
+ };
+ ExecuteProcedure("XD_TCS_AGV_操作记录_增加", parameters);
+ }
+ catch (Exception ex)
+ {
+ SaveMesLog("TCS操作记录", ex.Message, MesLogType.ERROR);
+ }
+ }
+
+ private static TCSAGV_CallResult PostToTcs(TCSAGV_Config config, string interfaceName,
+ string path, object request)
+ {
+ string url = config.BaseUrl.TrimEnd('/') + path;
+ string requestText = JsonConvert.SerializeObject(request);
+ SaveLog_Interface_Request("TCS_" + interfaceName, 1, requestText, out int aid);
+ string responseText;
+ using (HttpClient client = CreateHttpClient(config))
+ using (StringContent content = new StringContent(requestText, Encoding.UTF8, "application/json"))
+ {
+ HttpResponseMessage response = client.PostAsync(url, content).GetAwaiter().GetResult();
+ responseText = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
+ if (string.IsNullOrWhiteSpace(responseText) && !response.IsSuccessStatusCode)
+ {
+ responseText = "HTTP " + (int)response.StatusCode;
+ }
+ }
+ SaveLog_Interface_Response(responseText, aid);
+ return ParseCallResult(requestText, responseText);
+ }
+
+ private static TCSAGV_CallResult GetFromTcs(TCSAGV_Config config, string interfaceName,
+ string path, IDictionary query)
+ {
+ StringBuilder url = new StringBuilder(config.BaseUrl.TrimEnd('/') + path);
+ JObject request = new JObject();
+ if (query != null && query.Count > 0)
+ {
+ url.Append('?');
+ bool first = true;
+ foreach (KeyValuePair item in query)
+ {
+ if (!first) url.Append('&');
+ first = false;
+ url.Append(HttpUtility.UrlEncode(item.Key));
+ url.Append('=');
+ url.Append(HttpUtility.UrlEncode(item.Value ?? ""));
+ request[item.Key] = item.Value ?? "";
+ }
+ }
+
+ string requestText = request.ToString(Formatting.None);
+ bool skipInterfaceLog = string.Equals(interfaceName, "getMcrInfo",
+ StringComparison.OrdinalIgnoreCase);
+ int aid = 0;
+ if (!skipInterfaceLog)
+ {
+ SaveLog_Interface_Request("TCS_" + interfaceName, 1, requestText, out aid);
+ }
+ string responseText;
+ using (HttpClient client = CreateHttpClient(config))
+ {
+ HttpResponseMessage response = client.GetAsync(url.ToString()).GetAwaiter().GetResult();
+ responseText = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
+ if (string.IsNullOrWhiteSpace(responseText) && !response.IsSuccessStatusCode)
+ {
+ responseText = "HTTP " + (int)response.StatusCode;
+ }
+ }
+ if (!skipInterfaceLog)
+ {
+ SaveLog_Interface_Response(responseText, aid);
+ }
+ return ParseCallResult(requestText, responseText);
+ }
+
+ private static HttpClient CreateHttpClient(TCSAGV_Config config)
+ {
+ return new HttpClient
+ {
+ Timeout = TimeSpan.FromSeconds(config.HttpTimeoutSeconds > 0 ? config.HttpTimeoutSeconds : 15)
+ };
+ }
+
+ private static TCSAGV_CallResult ParseCallResult(string requestText, string responseText)
+ {
+ JObject response;
+ try
+ {
+ response = JObject.Parse(responseText ?? "");
+ }
+ catch (Exception ex)
+ {
+ return new TCSAGV_CallResult
+ {
+ Success = false,
+ Code = 500,
+ Message = "TCS返回非JSON响应:" + ex.Message,
+ RequestText = requestText,
+ ResponseText = responseText,
+ Response = null
+ };
+ }
+
+ bool successFlag = false;
+ bool hasSuccessFlag = response["success"] != null;
+ if (hasSuccessFlag)
+ {
+ bool.TryParse(Convert.ToString(response["success"]), out successFlag);
+ }
+ int code = ToInt(response["code"], successFlag ? 200 : 500);
+ bool success = hasSuccessFlag ? successFlag && code == 200 : code == 200;
+ string message = Convert.ToString(response["message"] ?? response["msg"] ?? response["result"] ?? "");
+ return new TCSAGV_CallResult
+ {
+ Success = success,
+ Code = code,
+ Message = message,
+ RequestText = requestText,
+ ResponseText = responseText,
+ Response = response
+ };
+ }
+
+ private static JObject FirstResultObject(JObject response)
+ {
+ if (response == null || response["result"] == null) return null;
+ JToken result = response["result"];
+ if (result.Type == JTokenType.Array)
+ {
+ JArray array = (JArray)result;
+ return array.Count > 0 ? array[0] as JObject : null;
+ }
+ return result as JObject;
+ }
+
+ private static DataTable ExecuteProcedure(string procedureName, SqlParameter[] parameters)
+ {
+ SqlParameter[] procedureParameters = parameters ?? new SqlParameter[0];
+ DataLinkMesWork2.DataAccess2.ExecuteStoredProcedure(procedureName, ref procedureParameters,
+ out DataTable table, out string errorMessage);
+ if (!string.IsNullOrWhiteSpace(errorMessage))
+ {
+ throw new Exception(errorMessage);
+ }
+ return table ?? new DataTable();
+ }
+
+ private static void EnsureBusinessSuccess(DataTable table)
+ {
+ if (table == null || table.Rows.Count == 0)
+ {
+ throw new Exception("数据库未返回业务结果");
+ }
+ if (ToInt(table.Rows[0]["result"], 0) != 1)
+ {
+ string message = table.Columns.Contains("msg") ? Convert.ToString(table.Rows[0]["msg"]) : "业务操作失败";
+ throw new Exception(message);
+ }
+ }
+
+ private static string NormalizeAction(string actionType)
+ {
+ string value = (actionType ?? "").Trim();
+ if (value == "插" || value.Equals("plug", StringComparison.OrdinalIgnoreCase)) return "插";
+ if (value == "拔" || value.Equals("pull", StringComparison.OrdinalIgnoreCase)) return "拔";
+ throw new Exception("任务动作只支持插或拔");
+ }
+
+ private static string NormalizePortCombination(string portCombination)
+ {
+ string value = (portCombination ?? "").Trim().ToUpperInvariant();
+ if (value == "XSD" || value == "XSE" || value == "XSD+XSE") return value;
+ throw new Exception("动作组合只支持XSD、XSE或XSD+XSE");
+ }
+
+ private static bool IsConfiguredProgram(TCSAGV_PointConfig point, string programName)
+ {
+ return programName == point.InsertXsdProgram || programName == point.InsertXseProgram
+ || programName == point.PullXsdProgram || programName == point.PullXseProgram;
+ }
+
+ private static string CreateExternalCode(string prefix)
+ {
+ return "XD_" + prefix + "_" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + "_"
+ + Guid.NewGuid().ToString("N").Substring(0, 6).ToUpperInvariant();
+ }
+
+ private static int ToInt(object value, int defaultValue)
+ {
+ int result;
+ return int.TryParse(Convert.ToString(value), out result) ? result : defaultValue;
+ }
+
+ private static bool ToBool(object value)
+ {
+ string text = Convert.ToString(value);
+ return text == "1" || text.Equals("true", StringComparison.OrdinalIgnoreCase);
+ }
+ }
+}
diff --git a/Interface_WebAPI/SlMesDbIterface/WebAPI/AGV/TCSAGV_Models.cs b/Interface_WebAPI/SlMesDbIterface/WebAPI/AGV/TCSAGV_Models.cs
new file mode 100644
index 0000000..7f88f38
--- /dev/null
+++ b/Interface_WebAPI/SlMesDbIterface/WebAPI/AGV/TCSAGV_Models.cs
@@ -0,0 +1,133 @@
+using Newtonsoft.Json.Linq;
+
+namespace MisDataSaveDate
+{
+ public class TCSAGV_TaskRequest
+ {
+ public string externalCode { get; set; }
+ public string type { get; set; }
+ public int priority { get; set; }
+ public string param { get; set; }
+ public string mcrgroup { get; set; }
+ public string mcrip { get; set; }
+ public string operatoruserid { get; set; }
+ }
+
+ public class TCSAGV_TaskStatusPushRequest
+ {
+ public string externalCode { get; set; }
+ public string status { get; set; }
+ public string ip { get; set; }
+ public string type { get; set; }
+ public string beginTime { get; set; }
+ public string endTime { get; set; }
+ public string remark { get; set; }
+ }
+
+ public class TCSAGV_PositionPushRequest
+ {
+ public string ip { get; set; }
+ public string currentPos { get; set; }
+ public string objectivePos { get; set; }
+ }
+
+ public class TCSAGV_StationDispatchRequest
+ {
+ public string stationCode { get; set; }
+ public string actionType { get; set; }
+ public string portCombination { get; set; }
+ public string operatorCode { get; set; }
+ public string parentExternalCode { get; set; }
+ }
+
+ public class TCSAGV_StepDispatchRequest
+ {
+ public string stationCode { get; set; }
+ public string programName { get; set; }
+ public string operatorCode { get; set; }
+ public string parentExternalCode { get; set; }
+ }
+
+ public class TCSAGV_TaskOperationRequest
+ {
+ public string externalCode { get; set; }
+ public string operatorCode { get; set; }
+ public string remark { get; set; }
+ public bool notifyPlc { get; set; }
+ }
+
+ public class TCSAGV_RobotOperationRequest
+ {
+ public string externalCode { get; set; }
+ public string ip { get; set; }
+ public string operatorCode { get; set; }
+ public string remark { get; set; }
+ }
+
+ public class TCSAGV_Config
+ {
+ public string BaseUrl { get; set; }
+ public bool Enabled { get; set; }
+ public string McrGroup { get; set; }
+ public string McrIp { get; set; }
+ public string OperatorUserId { get; set; }
+ public int DefaultPriority { get; set; }
+ public int HttpTimeoutSeconds { get; set; }
+ }
+
+ public class TCSAGV_PointConfig
+ {
+ public string LineCode { get; set; }
+ public string StationCode { get; set; }
+ public string PositionName { get; set; }
+ public string WorkStation { get; set; }
+ public string InsertXsdProgram { get; set; }
+ public string InsertXseProgram { get; set; }
+ public string PullXsdProgram { get; set; }
+ public string PullXseProgram { get; set; }
+ }
+
+ public class TCSAGV_CallResult
+ {
+ public bool Success { get; set; }
+ public int Code { get; set; }
+ public string Message { get; set; }
+ public string ExternalCode { get; set; }
+ public string RequestText { get; set; }
+ public string ResponseText { get; set; }
+ public JObject Response { get; set; }
+ }
+
+ public class TCSAGV_ApiResponse
+ {
+ public bool success { get; set; }
+ public int code { get; set; }
+ public string message { get; set; }
+ public object data { get; set; }
+ public long timestamp { get; set; }
+
+ public static TCSAGV_ApiResponse Ok(string message, object data = null)
+ {
+ return new TCSAGV_ApiResponse
+ {
+ success = true,
+ code = 200,
+ message = message,
+ data = data,
+ timestamp = System.DateTimeOffset.Now.ToUnixTimeMilliseconds()
+ };
+ }
+
+ public static TCSAGV_ApiResponse Fail(string message)
+ {
+ return new TCSAGV_ApiResponse
+ {
+ success = false,
+ code = 500,
+ message = message,
+ data = null,
+ timestamp = System.DateTimeOffset.Now.ToUnixTimeMilliseconds()
+ };
+ }
+ }
+}
diff --git a/Interface_WebAPI/SlMesDbIterface/WebAPI/Controller/AGVController.cs b/Interface_WebAPI/SlMesDbIterface/WebAPI/Controller/AGVController.cs
index e2c2be2..8d3e974 100644
--- a/Interface_WebAPI/SlMesDbIterface/WebAPI/Controller/AGVController.cs
+++ b/Interface_WebAPI/SlMesDbIterface/WebAPI/Controller/AGVController.cs
@@ -40,5 +40,202 @@ namespace MisDataSaveDate
};
}
+ ///
+ /// 临工AGV到达/离开状态回调接口。
+ ///
+ /// 临工AGV状态回调数据
+ /// 处理结果
+ [HttpPost]
+ [Route("agv/lgagv/statusUpdate")]
+ public HttpResponseMessage LGAGV_StatusUpdate([FromBody] JObject jobj)
+ {
+ return new HttpResponseMessage
+ {
+ Content = new StringContent(LGAGV_BusinessLogic.ProcessStatusUpdate(headUrl + System.Reflection.MethodBase.GetCurrentMethod().Name, jobj), Encoding.GetEncoding("UTF-8"), "application/json")
+ };
+ }
+
+ ///
+ /// 临工AGV任务结果回调接口。
+ ///
+ /// 临工AGV任务结果数据
+ /// 处理结果
+ [HttpPost]
+ [Route("agv/lgagv/taskResult")]
+ public HttpResponseMessage LGAGV_TaskResult([FromBody] JObject jobj)
+ {
+ return new HttpResponseMessage
+ {
+ Content = new StringContent(LGAGV_BusinessLogic.ProcessTaskResult(headUrl + System.Reflection.MethodBase.GetCurrentMethod().Name, jobj), Encoding.GetEncoding("UTF-8"), "application/json")
+ };
+ }
+
+ [HttpPost]
+ [Route("api/reportExternalBusinessTaskStatus")]
+ public HttpResponseMessage TCS_ReportExternalBusinessTaskStatus([FromBody] JObject jobj)
+ {
+ return JsonResponse(TCSAGV_BusinessLogic.ProcessTaskStatus("api/reportExternalBusinessTaskStatus", jobj));
+ }
+
+ [HttpPost]
+ [Route("api/mcrInfoLocation")]
+ public HttpResponseMessage TCS_McrInfoLocation([FromBody] JObject jobj)
+ {
+ return JsonResponse(TCSAGV_BusinessLogic.ProcessPositionPush("api/mcrInfoLocation", jobj));
+ }
+
+ [HttpPost]
+ [Route("agv/tcs/dispatchStation")]
+ public HttpResponseMessage TCS_DispatchStation([FromBody] TCSAGV_StationDispatchRequest request)
+ {
+ return ExecuteTcs(() =>
+ {
+ if (request == null) throw new Exception("请求参数不能为空");
+ if (string.IsNullOrWhiteSpace(request.portCombination)) throw new Exception("请选择动作组合");
+ TCSAGV_CallResult result = TCSAGV_BusinessLogic.DispatchStationTask(
+ request.stationCode, request.actionType, request.portCombination, "MANUAL", request.operatorCode,
+ request.parentExternalCode, false);
+ return result.Success
+ ? TCSAGV_ApiResponse.Ok("任务下发成功", result)
+ : TCSAGV_ApiResponse.Fail(result.Message);
+ });
+ }
+
+ [HttpPost]
+ [Route("agv/tcs/dispatchStep")]
+ public HttpResponseMessage TCS_DispatchStep([FromBody] TCSAGV_StepDispatchRequest request)
+ {
+ return ExecuteTcs(() =>
+ {
+ if (request == null) throw new Exception("请求参数不能为空");
+ TCSAGV_CallResult result = TCSAGV_BusinessLogic.DispatchSingleStep(
+ request.stationCode, request.programName, request.operatorCode, request.parentExternalCode);
+ return result.Success
+ ? TCSAGV_ApiResponse.Ok("单步任务下发成功", result)
+ : TCSAGV_ApiResponse.Fail(result.Message);
+ });
+ }
+
+ [HttpPost]
+ [Route("agv/tcs/retry")]
+ public HttpResponseMessage TCS_Retry([FromBody] TCSAGV_TaskOperationRequest request)
+ {
+ return ExecuteTcs(() =>
+ {
+ if (request == null) throw new Exception("请求参数不能为空");
+ TCSAGV_CallResult result = TCSAGV_BusinessLogic.RetryTask(request.externalCode, request.operatorCode);
+ return result.Success
+ ? TCSAGV_ApiResponse.Ok("整单重发成功", result)
+ : TCSAGV_ApiResponse.Fail(result.Message);
+ });
+ }
+
+ [HttpPost]
+ [Route("agv/tcs/cancel")]
+ public HttpResponseMessage TCS_Cancel([FromBody] TCSAGV_TaskOperationRequest request)
+ {
+ return ExecuteTcs(() =>
+ {
+ if (request == null) throw new Exception("请求参数不能为空");
+ TCSAGV_CallResult result = TCSAGV_BusinessLogic.CancelTask(request.externalCode, request.operatorCode);
+ return result.Success ? TCSAGV_ApiResponse.Ok("任务取消成功", result) : TCSAGV_ApiResponse.Fail(result.Message);
+ });
+ }
+
+ [HttpPost]
+ [Route("agv/tcs/stopPending")]
+ public HttpResponseMessage TCS_StopPending([FromBody] TCSAGV_TaskOperationRequest request)
+ {
+ return ExecuteTcs(() =>
+ {
+ string externalCode = request == null ? "" : request.externalCode;
+ string operatorCode = request == null ? "" : request.operatorCode;
+ TCSAGV_CallResult result = TCSAGV_BusinessLogic.StopPendingTasks(externalCode, operatorCode);
+ return result.Success ? TCSAGV_ApiResponse.Ok("未开始任务已停止", result) : TCSAGV_ApiResponse.Fail(result.Message);
+ });
+ }
+
+ [HttpPost]
+ [Route("agv/tcs/reset")]
+ public HttpResponseMessage TCS_Reset([FromBody] TCSAGV_RobotOperationRequest request)
+ {
+ return ExecuteTcs(() =>
+ {
+ if (request == null) throw new Exception("请求参数不能为空");
+ TCSAGV_CallResult result = TCSAGV_BusinessLogic.ResetRobot(
+ request.externalCode, request.ip, request.operatorCode);
+ return result.Success ? TCSAGV_ApiResponse.Ok("移动机器人复位成功", result) : TCSAGV_ApiResponse.Fail(result.Message);
+ });
+ }
+
+ [HttpPost]
+ [Route("agv/tcs/stopNow")]
+ public HttpResponseMessage TCS_StopNow([FromBody] TCSAGV_RobotOperationRequest request)
+ {
+ return ExecuteTcs(() =>
+ {
+ if (request == null) throw new Exception("请求参数不能为空");
+ TCSAGV_CallResult result = TCSAGV_BusinessLogic.StopNow(
+ request.externalCode, request.ip, request.operatorCode);
+ return result.Success ? TCSAGV_ApiResponse.Ok("移动机器人已紧急停止", result) : TCSAGV_ApiResponse.Fail(result.Message);
+ });
+ }
+
+ [HttpPost]
+ [Route("agv/tcs/syncTask")]
+ public HttpResponseMessage TCS_SyncTask([FromBody] TCSAGV_TaskOperationRequest request)
+ {
+ return ExecuteTcs(() =>
+ {
+ if (request == null) throw new Exception("请求参数不能为空");
+ TCSAGV_CallResult result = TCSAGV_BusinessLogic.SyncTaskStatus(request.externalCode, request.operatorCode);
+ return result.Success ? TCSAGV_ApiResponse.Ok("任务状态同步成功", result) : TCSAGV_ApiResponse.Fail(result.Message);
+ });
+ }
+
+ [HttpGet]
+ [Route("agv/tcs/queryRobot")]
+ public HttpResponseMessage TCS_QueryRobot()
+ {
+ return ExecuteTcs(() =>
+ {
+ TCSAGV_CallResult result = TCSAGV_BusinessLogic.QueryRobotInfo();
+ return result.Success ? TCSAGV_ApiResponse.Ok("机器人状态查询成功", result.Response) : TCSAGV_ApiResponse.Fail(result.Message);
+ });
+ }
+
+ [HttpPost]
+ [Route("agv/tcs/handleFailure")]
+ public HttpResponseMessage TCS_HandleFailure([FromBody] TCSAGV_TaskOperationRequest request)
+ {
+ return ExecuteTcs(() =>
+ {
+ if (request == null) throw new Exception("请求参数不能为空");
+ TCSAGV_BusinessLogic.HandleFailure(request.externalCode, request.operatorCode, request.remark, request.notifyPlc);
+ return TCSAGV_ApiResponse.Ok("失败任务已人工处理");
+ });
+ }
+
+ private static HttpResponseMessage ExecuteTcs(Func action)
+ {
+ TCSAGV_ApiResponse response;
+ try
+ {
+ response = action();
+ }
+ catch (Exception ex)
+ {
+ response = TCSAGV_ApiResponse.Fail(ex.Message);
+ }
+ return JsonResponse(JsonConvert.SerializeObject(response));
+ }
+
+ private static HttpResponseMessage JsonResponse(string json)
+ {
+ return new HttpResponseMessage
+ {
+ Content = new StringContent(json, Encoding.GetEncoding("UTF-8"), "application/json")
+ };
+ }
}
}
diff --git a/Interface_WebAPI/SlMesDbIterface/WebAPI/Controller/WEBController.cs b/Interface_WebAPI/SlMesDbIterface/WebAPI/Controller/WEBController.cs
index ba93382..b640c57 100644
--- a/Interface_WebAPI/SlMesDbIterface/WebAPI/Controller/WEBController.cs
+++ b/Interface_WebAPI/SlMesDbIterface/WebAPI/Controller/WEBController.cs
@@ -99,6 +99,36 @@ namespace MisDataSaveDate
};
}
+ ///
+ /// 临工AGV网页按钮调度接口。
+ ///
+ /// 调度请求数据
+ /// 处理结果
+ [HttpPost]
+ [Route("web/LGAGV_Dispatch")]
+ public HttpResponseMessage LGAGV_Dispatch([FromBody] JObject jobj)
+ {
+ return new HttpResponseMessage
+ {
+ Content = new StringContent(LGWEB_BusinessLogic.WEB_DispatchAGV(headUrl + System.Reflection.MethodBase.GetCurrentMethod().Name, jobj), Encoding.GetEncoding("UTF-8"), "application/json")
+ };
+ }
+
+ ///
+ /// 临工AGV网页按钮放行接口。
+ ///
+ /// 放行请求数据
+ /// 处理结果
+ [HttpPost]
+ [Route("web/LGAGV_AllowLeave")]
+ public HttpResponseMessage LGAGV_AllowLeave([FromBody] JObject jobj)
+ {
+ return new HttpResponseMessage
+ {
+ Content = new StringContent(LGWEB_BusinessLogic.WEB_AllowLeave(headUrl + System.Reflection.MethodBase.GetCurrentMethod().Name, jobj), Encoding.GetEncoding("UTF-8"), "application/json")
+ };
+ }
+
///
/// 批量读取PLC点位值接口
/// 前端传递TagTypeCodeID数组和工位号,返回对应点位的值
@@ -131,4 +161,4 @@ namespace MisDataSaveDate
};
}
}
-}
\ No newline at end of file
+}
diff --git a/Interface_WebAPI/SlMesDbIterface/WebAPI/FactoryMES/CALL_Inerface_DataHandle.cs b/Interface_WebAPI/SlMesDbIterface/WebAPI/FactoryMES/CALL_Inerface_DataHandle.cs
index e03c662..201f3f0 100644
--- a/Interface_WebAPI/SlMesDbIterface/WebAPI/FactoryMES/CALL_Inerface_DataHandle.cs
+++ b/Interface_WebAPI/SlMesDbIterface/WebAPI/FactoryMES/CALL_Inerface_DataHandle.cs
@@ -1,4 +1,4 @@
-using DataLinkMesWork;
+using DataLinkMesWork;
using MesServerWork;
using MisDataFunDll;
using MisDataSaveDate.MOM;
@@ -24,6 +24,17 @@ namespace WebApi
{
public class CALL_Inerface_DataHandle
{
+ private static bool TryCheckProcessReportAllowed(string opName, string engineID, string orderForm, string reqType)
+ {
+ DataTable isSendDT = MisDataFun.IOT_ReqData_UpLoad_IsSend(opName, orderForm, reqType, engineID);
+ if (isSendDT.Rows[0]["result"].ToString() != "1")
+ {
+ SaveMesLog($"工序{reqType}", isSendDT.Rows[0]["msg"].ToString(), MesLogType.ERROR);
+ return false;
+ }
+ return true;
+ }
+
///
/// 开工(工序)
///
@@ -32,19 +43,17 @@ namespace WebApi
///
public static void CALL_Inerface_JobStart(string OpName, string EngineID, string OrderForm)
{
- DataTable IsSendDT = MisDataFun.IOT_ReqData_UpLoad_IsSend(OpName, OrderForm, "完工");
- if (IsSendDT.Rows[0]["result"].ToString() != "1")
+ if (!TryCheckProcessReportAllowed(OpName, EngineID, OrderForm, "开工"))
{
- SaveMesLog("工序完工", IsSendDT.Rows[0]["msg"].ToString(), MesLogType.ERROR);
return;
}
//查询工单ID、任务ID、工序ID
var param = new SqlParameter[] {
- new SqlParameter("@订单号",OrderForm),
- new SqlParameter("@工序号",OpName),
- new SqlParameter("@工件编号",EngineID)
- };
+ new SqlParameter("@订单号",OrderForm),
+ new SqlParameter("@工序号",OpName),
+ new SqlParameter("@工件编号",EngineID)
+ };
DataLinkMesWork2.DataAccess2.ExecuteStoredProcedure("CallWebApi_FactoryMes_JobStart", ref param, out DataTable dt, out string errorMessage);
if (dt.Rows.Count > 0)
@@ -101,19 +110,17 @@ namespace WebApi
///
public static void CALL_Inerface_JobFinished(string OpName, string EngineID, string OrderForm)
{
- DataTable IsSendDT = MisDataFun.IOT_ReqData_UpLoad_IsSend(OpName, OrderForm, "完工");
- if (IsSendDT.Rows[0]["result"].ToString() != "1")
+ if (!TryCheckProcessReportAllowed(OpName, EngineID, OrderForm, "完工"))
{
- SaveMesLog("工序完工", IsSendDT.Rows[0]["msg"].ToString(), MesLogType.ERROR);
return;
}
//查询工单ID、任务ID、工序ID
var param = new SqlParameter[] {
- new SqlParameter("@订单号",OrderForm),
- new SqlParameter("@工序号",OpName),
- new SqlParameter("@工件编号",EngineID)
- };
+ new SqlParameter("@订单号",OrderForm),
+ new SqlParameter("@工序号",OpName),
+ new SqlParameter("@工件编号",EngineID)
+ };
//DataAccess.ExecuteStoredProcedure("CallWebApi_FactoryMes_JobFinished", ref param, out DataSet ds);
DataLinkMesWork2.DataAccess2.ExecuteStoredProcedure("CallWebApi_FactoryMes_JobFinished", ref param, out DataSet ds, out string errorMessage);
//a.[订单ID],[任务ID],[工序ID],[工序名称],1 as 完工数量,@开工时间 as 开工时间,@结束时间 as 结束时间,@节拍 as 节拍,@操作时间 as 最后修改时间
@@ -283,6 +290,7 @@ namespace WebApi
orderID = dt.Rows[0]["订单ID"].ToString();
taskID = dt.Rows[0]["任务ID"].ToString();
}
+
// 非工厂下发的订单产品,不上传报警
if (string.IsNullOrEmpty(orderID) || string.IsNullOrEmpty(taskID)) return;
@@ -567,6 +575,15 @@ namespace WebApi
}
}
+ ///
+ /// 物料转序
+ ///
+ /// 起始位置编号
+ /// 终点位置编号
+ /// 派工任务id
+ /// 搬运类型:1:搬运容器,2:搬运货架
+ /// 载具类型:0-空;1-满
+ /// 返回调用结果 JObject,包含 code 和 message
///
/// 物料转序
///
@@ -940,3 +957,4 @@ namespace WebApi
//#endregion
}
}
+
diff --git a/Interface_WebAPI/SlMesDbIterface/WebAPI/WEB/LGWEB_BusinessLogic.cs b/Interface_WebAPI/SlMesDbIterface/WebAPI/WEB/LGWEB_BusinessLogic.cs
new file mode 100644
index 0000000..b438a21
--- /dev/null
+++ b/Interface_WebAPI/SlMesDbIterface/WebAPI/WEB/LGWEB_BusinessLogic.cs
@@ -0,0 +1,471 @@
+using System;
+using System.Data;
+using System.Data.SqlClient;
+using System.Web.Http;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using MisDataSaveDate.Helper;
+using static MisDataSaveDate.Helper.ApiLogHelper;
+
+namespace MisDataSaveDate
+{
+ ///
+ /// 临工AGV网页按钮业务处理。
+ ///
+ public class LGWEB_BusinessLogic
+ {
+ ///
+ /// 网页按钮下发临工AGV搬运任务。
+ ///
+ public static string WEB_DispatchAGV(string url, [FromBody] JObject jobj)
+ {
+ var result = new MsgResHeader