第一次提交
This commit is contained in:
@ -0,0 +1,108 @@
|
||||
package com.fastbee.data.quartz;
|
||||
|
||||
import com.fastbee.common.constant.Constants;
|
||||
import com.fastbee.common.constant.ScheduleConstants;
|
||||
import com.fastbee.common.utils.ExceptionUtil;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
import com.fastbee.common.utils.bean.BeanUtils;
|
||||
import com.fastbee.common.utils.spring.SpringUtils;
|
||||
import com.fastbee.iot.domain.DeviceJob;
|
||||
import com.fastbee.quartz.domain.SysJobLog;
|
||||
import com.fastbee.quartz.service.ISysJobLogService;
|
||||
import org.quartz.Job;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 抽象quartz调用
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public abstract class AbstractQuartzJob implements Job
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class);
|
||||
|
||||
/**
|
||||
* 线程本地变量
|
||||
*/
|
||||
private static ThreadLocal<Date> threadLocal = new ThreadLocal<>();
|
||||
|
||||
@Override
|
||||
public void execute(JobExecutionContext context) throws JobExecutionException
|
||||
{
|
||||
DeviceJob deviceJob = new DeviceJob();
|
||||
BeanUtils.copyBeanProp(deviceJob, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES));
|
||||
try
|
||||
{
|
||||
before(context, deviceJob);
|
||||
if (deviceJob != null)
|
||||
{
|
||||
doExecute(context, deviceJob);
|
||||
}
|
||||
after(context, deviceJob, null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("任务执行异常 - :", e);
|
||||
after(context, deviceJob, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行前
|
||||
*
|
||||
* @param context 工作执行上下文对象
|
||||
* @param deviceJob 系统计划任务
|
||||
*/
|
||||
protected void before(JobExecutionContext context, DeviceJob deviceJob)
|
||||
{
|
||||
threadLocal.set(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行后
|
||||
*
|
||||
* @param context 工作执行上下文对象
|
||||
* @param deviceJob 系统计划任务
|
||||
*/
|
||||
protected void after(JobExecutionContext context, DeviceJob deviceJob, Exception e)
|
||||
{
|
||||
Date startTime = threadLocal.get();
|
||||
threadLocal.remove();
|
||||
|
||||
final SysJobLog sysJobLog = new SysJobLog();
|
||||
sysJobLog.setJobName(deviceJob.getJobName());
|
||||
sysJobLog.setJobGroup(deviceJob.getJobGroup());
|
||||
sysJobLog.setInvokeTarget(deviceJob.getDeviceName());
|
||||
sysJobLog.setStartTime(startTime);
|
||||
sysJobLog.setStopTime(new Date());
|
||||
long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime();
|
||||
sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒");
|
||||
if (e != null)
|
||||
{
|
||||
sysJobLog.setStatus(Constants.FAIL);
|
||||
String errorMsg = StringUtils.substring(ExceptionUtil.getExceptionMessage(e), 0, 2000);
|
||||
sysJobLog.setExceptionInfo(errorMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
sysJobLog.setStatus(Constants.SUCCESS);
|
||||
}
|
||||
|
||||
// 写入数据库当中
|
||||
SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行方法,由子类重载
|
||||
*
|
||||
* @param context 工作执行上下文对象
|
||||
* @param deviceJob 系统计划任务
|
||||
* @throws Exception 执行过程中的异常
|
||||
*/
|
||||
protected abstract void doExecute(JobExecutionContext context, DeviceJob deviceJob) throws Exception;
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.fastbee.data.quartz;
|
||||
|
||||
import org.quartz.CronExpression;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* cron表达式工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*
|
||||
*/
|
||||
public class CronUtils
|
||||
{
|
||||
/**
|
||||
* 返回一个布尔值代表一个给定的Cron表达式的有效性
|
||||
*
|
||||
* @param cronExpression Cron表达式
|
||||
* @return boolean 表达式是否有效
|
||||
*/
|
||||
public static boolean isValid(String cronExpression)
|
||||
{
|
||||
return CronExpression.isValidExpression(cronExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个字符串值,表示该消息无效Cron表达式给出有效性
|
||||
*
|
||||
* @param cronExpression Cron表达式
|
||||
* @return String 无效时返回表达式错误描述,如果有效返回null
|
||||
*/
|
||||
public static String getInvalidMessage(String cronExpression)
|
||||
{
|
||||
try
|
||||
{
|
||||
new CronExpression(cronExpression);
|
||||
return null;
|
||||
}
|
||||
catch (ParseException pe)
|
||||
{
|
||||
return pe.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回下一个执行时间根据给定的Cron表达式
|
||||
*
|
||||
* @param cronExpression Cron表达式
|
||||
* @return Date 下次Cron表达式执行时间
|
||||
*/
|
||||
public static Date getNextExecution(String cronExpression)
|
||||
{
|
||||
try
|
||||
{
|
||||
CronExpression cron = new CronExpression(cronExpression);
|
||||
return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,173 @@
|
||||
package com.fastbee.data.quartz;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.fastbee.base.service.ISessionStore;
|
||||
import com.fastbee.base.session.Session;
|
||||
import com.fastbee.common.core.device.DeviceAndProtocol;
|
||||
import com.fastbee.common.core.mq.message.ModbusPollMsg;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
||||
import com.fastbee.common.enums.DeviceStatus;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
import com.fastbee.common.utils.spring.SpringUtils;
|
||||
import com.fastbee.iot.domain.*;
|
||||
import com.fastbee.iot.enums.DeviceType;
|
||||
import com.fastbee.iot.mapper.*;
|
||||
import com.fastbee.iot.model.Action;
|
||||
import com.fastbee.iot.model.DeviceStatusVO;
|
||||
import com.fastbee.iot.model.ModbusJobBo;
|
||||
import com.fastbee.iot.model.ModbusPollBo;
|
||||
import com.fastbee.iot.service.*;
|
||||
import com.fastbee.mq.redischannel.producer.MessageProducer;
|
||||
import com.fastbee.mq.ruleEngine.SceneContext;
|
||||
import com.fastbee.mq.service.IDataHandler;
|
||||
import com.fastbee.mq.service.IMqttMessagePublish;
|
||||
import com.fastbee.mqtt.manager.MqttRemoteManager;
|
||||
import com.fastbee.ruleEngine.core.FlowLogExecutor;
|
||||
import com.google.common.annotations.GwtCompatible;
|
||||
import com.yomahub.liteflow.core.FlowExecutor;
|
||||
import com.yomahub.liteflow.flow.LiteflowResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 任务执行工具
|
||||
*
|
||||
* @author kerwincui
|
||||
*/
|
||||
@Slf4j
|
||||
public class JobInvokeUtil {
|
||||
|
||||
/**
|
||||
* 获取消息推送接口
|
||||
*/
|
||||
private static IMqttMessagePublish messagePublish = SpringUtils.getBean(IMqttMessagePublish.class);
|
||||
|
||||
private static SceneMapper sceneMapper = SpringUtils.getBean(SceneMapper.class);
|
||||
|
||||
private static FlowLogExecutor flowExecutor = SpringUtils.getBean(FlowLogExecutor.class);
|
||||
|
||||
private static IDataHandler dataHandler = SpringUtils.getBean(IDataHandler.class);
|
||||
|
||||
private static IDeviceService deviceService = SpringUtils.getBean(IDeviceService.class);
|
||||
|
||||
private static IModbusJobService modbusJobService = SpringUtils.getBean(IModbusJobService.class);
|
||||
|
||||
private static IModbusParamsService modbusParamsService = SpringUtils.getBean(IModbusParamsService.class);
|
||||
|
||||
private static ISessionStore sessionStore = SpringUtils.getBean(ISessionStore.class);
|
||||
|
||||
private static MqttRemoteManager mqttRemoteManager = SpringUtils.getBean(MqttRemoteManager.class);
|
||||
|
||||
/**
|
||||
* 执行方法
|
||||
*
|
||||
* @param deviceJob 系统任务
|
||||
*/
|
||||
public static void invokeMethod(DeviceJob deviceJob) throws Exception {
|
||||
if (deviceJob.getJobType() == 1) {
|
||||
System.out.println("------------------------执行定时任务-----------------------------");
|
||||
List<Action> actions = JSON.parseArray(deviceJob.getActions(), Action.class);
|
||||
List<ThingsModelSimpleItem> propertys = new ArrayList<>();
|
||||
List<ThingsModelSimpleItem> functions = new ArrayList<>();
|
||||
for (int i = 0; i < actions.size(); i++) {
|
||||
ThingsModelSimpleItem model = new ThingsModelSimpleItem();
|
||||
model.setId(actions.get(i).getId());
|
||||
model.setValue(actions.get(i).getValue());
|
||||
model.setRemark("设备定时");
|
||||
if (actions.get(i).getType() == 1) {
|
||||
propertys.add(model);
|
||||
} else if (actions.get(i).getType() == 2) {
|
||||
functions.add(model);
|
||||
}
|
||||
}
|
||||
// 发布属性
|
||||
if (propertys.size() > 0) {
|
||||
messagePublish.publishProperty(deviceJob.getProductId(), deviceJob.getSerialNumber(), propertys, 0);
|
||||
}
|
||||
// 发布功能
|
||||
if (functions.size() > 0) {
|
||||
messagePublish.publishFunction(deviceJob.getProductId(), deviceJob.getSerialNumber(), functions, 0);
|
||||
}
|
||||
|
||||
} else if (deviceJob.getJobType() == 3) {
|
||||
System.out.println("------------------[定时执行场景联动]---------------------");
|
||||
Scene scene=sceneMapper.selectSceneBySceneId(deviceJob.getSceneId());
|
||||
// 执行场景规则,异步非阻塞
|
||||
SceneContext context = new SceneContext("", 0L,0,null);
|
||||
flowExecutor.execute2Future(String.valueOf(scene.getChainName()), null, context);
|
||||
} else if (4 == deviceJob.getJobType()) {
|
||||
System.out.println("------------------[定时执行场景运算型变量]---------------------");
|
||||
String s = dataHandler.calculateSceneModelTagValue(deviceJob.getDatasourceId());
|
||||
if (StringUtils.isEmpty(s)) {
|
||||
System.out.println("------------------[定时执行场景运算型变量失败:+" + s + "]---------------------");
|
||||
}
|
||||
}else if (5 == deviceJob.getJobType()){
|
||||
log.info("----------------执行modbus轮训指令----------------------");
|
||||
Long jobId = deviceJob.getJobId();
|
||||
ModbusJob modbusJob = new ModbusJob();
|
||||
modbusJob.setJobId(jobId);
|
||||
modbusJob.setStatus("0");
|
||||
List<ModbusJob> modbusJobList = modbusJobService.selectModbusJobList(modbusJob);
|
||||
if (CollectionUtils.isEmpty(modbusJobList))return;
|
||||
//处理子设备情况
|
||||
handleSubDeviceStatus(modbusJobList);
|
||||
List<String> commandList = modbusJobList.stream().map(ModbusJob::getCommand).collect(Collectors.toList());
|
||||
ModbusPollMsg modbusPollMsg = new ModbusPollMsg();
|
||||
modbusPollMsg.setSerialNumber(deviceJob.getSerialNumber());
|
||||
modbusPollMsg.setProductId(deviceJob.getProductId());
|
||||
modbusPollMsg.setCommandList(commandList);
|
||||
DeviceStatusVO deviceStatusVO = deviceService.selectDeviceStatusAndTransportStatus(modbusPollMsg.getSerialNumber());
|
||||
modbusPollMsg.setTransport(deviceStatusVO.getTransport());
|
||||
if (deviceStatusVO.getStatus() != DeviceStatus.ONLINE.getType()){
|
||||
log.info("设备:[{}],不在线",modbusPollMsg.getSerialNumber());
|
||||
return;
|
||||
}
|
||||
log.info("执行modbus轮询指令:[{}]",JSON.toJSONString(commandList));
|
||||
MessageProducer.sendPropFetch(modbusPollMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO --耗时操作,这里后续放在MQ处理
|
||||
* 处理子设备根据设备数据定时更新状态
|
||||
*/
|
||||
public static void handleSubDeviceStatus(List<ModbusJob> modbusJobList){
|
||||
for (ModbusJob modbusJob : modbusJobList) {
|
||||
String subSerialNumber = modbusJob.getSubSerialNumber();
|
||||
Session session = sessionStore.getSession(subSerialNumber);
|
||||
if (Objects.isNull(session))continue;
|
||||
//如果是网关子设备,则检测子设备是否在特定时间内有数据上报
|
||||
if (modbusJob.getDeviceType() == DeviceType.SUB_GATEWAY.getCode()){
|
||||
long deterTime = 300L; //这里默认使用5分钟,如果轮询时间大于5分钟,请在配置参数设置更长时间
|
||||
ModbusParams params = modbusParamsService.getModbusParamsByDeviceId(modbusJob.getSubDeviceId());
|
||||
if (!Objects.isNull(params)){
|
||||
deterTime = Long.parseLong(params.getDeterTimer());
|
||||
}
|
||||
|
||||
long lastAccessTime = session.getLastAccessTime();
|
||||
//如果现在的时间 - 最后访问时间 > 判断时间则更新为离线
|
||||
long time = (System.currentTimeMillis() - lastAccessTime) / 1000;
|
||||
if (time > deterTime){
|
||||
//处理设备离线
|
||||
Device updateBo = new Device();
|
||||
updateBo.setStatus(DeviceStatus.OFFLINE.getType());
|
||||
updateBo.setSerialNumber(subSerialNumber);
|
||||
updateBo.setUpdateTime(DateUtils.getNowDate());
|
||||
deviceService.updateDeviceStatus(updateBo);
|
||||
mqttRemoteManager.pushDeviceStatus(params.getProductId(), subSerialNumber, DeviceStatus.OFFLINE);
|
||||
sessionStore.cleanSession(subSerialNumber);
|
||||
log.info("设备[{}],超过:[{}],未上报数据,更新为离线",subSerialNumber,deterTime);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.fastbee.data.quartz;
|
||||
|
||||
import com.fastbee.iot.domain.DeviceJob;
|
||||
import org.quartz.DisallowConcurrentExecution;
|
||||
import org.quartz.JobExecutionContext;
|
||||
|
||||
/**
|
||||
* 定时任务处理(禁止并发执行)
|
||||
*
|
||||
* @author ruoyi
|
||||
*
|
||||
*/
|
||||
@DisallowConcurrentExecution
|
||||
public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob
|
||||
{
|
||||
@Override
|
||||
protected void doExecute(JobExecutionContext context, DeviceJob deviceJob) throws Exception
|
||||
{
|
||||
JobInvokeUtil.invokeMethod(deviceJob);
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.fastbee.data.quartz;
|
||||
|
||||
import com.fastbee.iot.domain.DeviceJob;
|
||||
import org.quartz.JobExecutionContext;
|
||||
|
||||
/**
|
||||
* 定时任务处理(允许并发执行)
|
||||
*
|
||||
* @author ruoyi
|
||||
*
|
||||
*/
|
||||
public class QuartzJobExecution extends AbstractQuartzJob
|
||||
{
|
||||
@Override
|
||||
protected void doExecute(JobExecutionContext context, DeviceJob deviceJob) throws Exception
|
||||
{
|
||||
JobInvokeUtil.invokeMethod(deviceJob);
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
package com.fastbee.data.quartz;
|
||||
|
||||
import com.fastbee.common.constant.ScheduleConstants;
|
||||
import com.fastbee.common.exception.job.TaskException;
|
||||
import com.fastbee.common.exception.job.TaskException.Code;
|
||||
import com.fastbee.iot.domain.DeviceJob;
|
||||
import org.quartz.*;
|
||||
|
||||
/**
|
||||
* 定时任务工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*
|
||||
*/
|
||||
public class ScheduleUtils
|
||||
{
|
||||
/**
|
||||
* 得到quartz任务类
|
||||
*
|
||||
* @param deviceJob 执行计划
|
||||
* @return 具体执行任务类
|
||||
*/
|
||||
private static Class<? extends Job> getQuartzJobClass(DeviceJob deviceJob)
|
||||
{
|
||||
boolean isConcurrent = "0".equals(deviceJob.getConcurrent());
|
||||
return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建任务触发对象
|
||||
*/
|
||||
public static TriggerKey getTriggerKey(Long jobId, String jobGroup)
|
||||
{
|
||||
return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建任务键对象
|
||||
*/
|
||||
public static JobKey getJobKey(Long jobId, String jobGroup)
|
||||
{
|
||||
return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建定时任务
|
||||
*/
|
||||
public static void createScheduleJob(Scheduler scheduler, DeviceJob job) throws SchedulerException, TaskException
|
||||
{
|
||||
Class<? extends Job> jobClass = getQuartzJobClass(job);
|
||||
// 1.创建任务
|
||||
Long jobId = job.getJobId();
|
||||
String jobGroup = job.getJobGroup();
|
||||
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();
|
||||
|
||||
// 表达式调度构建器
|
||||
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
|
||||
cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);
|
||||
|
||||
// 2.创建触发器, 按新的cronExpression表达式构建一个新的trigger
|
||||
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup))
|
||||
.withSchedule(cronScheduleBuilder).build();
|
||||
|
||||
// 放入参数,运行时的方法可以获取
|
||||
jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);
|
||||
|
||||
// 判断是否存在
|
||||
if (scheduler.checkExists(getJobKey(jobId, jobGroup)))
|
||||
{
|
||||
// 防止创建时存在数据问题 先移除,然后在执行创建操作
|
||||
scheduler.deleteJob(getJobKey(jobId, jobGroup));
|
||||
}
|
||||
|
||||
// 3.任务和触发器添加到调度器
|
||||
scheduler.scheduleJob(jobDetail, trigger);
|
||||
|
||||
// 暂停任务
|
||||
if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue()))
|
||||
{
|
||||
scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置定时任务策略
|
||||
*/
|
||||
public static CronScheduleBuilder handleCronScheduleMisfirePolicy(DeviceJob job, CronScheduleBuilder cb)
|
||||
throws TaskException
|
||||
{
|
||||
switch (job.getMisfirePolicy())
|
||||
{
|
||||
case ScheduleConstants.MISFIRE_DEFAULT:
|
||||
return cb;
|
||||
case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
|
||||
return cb.withMisfireHandlingInstructionIgnoreMisfires();
|
||||
case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
|
||||
return cb.withMisfireHandlingInstructionFireAndProceed();
|
||||
case ScheduleConstants.MISFIRE_DO_NOTHING:
|
||||
return cb.withMisfireHandlingInstructionDoNothing();
|
||||
default:
|
||||
throw new TaskException("The task misfire policy '" + job.getMisfirePolicy()
|
||||
+ "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user