第一次提交

This commit is contained in:
wyw
2024-08-08 00:31:26 +08:00
commit c202e2b63d
1819 changed files with 221890 additions and 0 deletions

View File

@ -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;
}

View File

@ -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());
}
}
}

View File

@ -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);
}
}
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}