@idm-plugin/vessel 1.1.8 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- (function(j,y){typeof exports=="object"&&typeof module<"u"?y(exports,require("got"),require("@log4js-node/log4js-api"),require("moment")):typeof define=="function"&&define.amd?define(["exports","got","@log4js-node/log4js-api","moment"],y):(j=typeof globalThis<"u"?globalThis:j||self,y(j["idm-plugin-rabbitmq"]={},j.got,j["@log4js-node/log4js-api"],j.moment))})(this,function(j,y,E,g){"use strict";var U=Object.defineProperty;var J=(j,y,E)=>y in j?U(j,y,{enumerable:!0,configurable:!0,writable:!0,value:E}):j[y]=E;var A=(j,y,E)=>(J(j,typeof y!="symbol"?y+"":y,E),E);const b=E.getLogger("vessel");class V{parseStatus(p){let e,a;switch(p){case 0:e="在航(主机推动)",a="The engine is in use";break;case 1:e="锚泊",a="Anchored";break;case 2:e="失控",a="Not operated";break;case 3:e="操纵受限",a="Limited airworthiness";break;case 4:e="吃水受限",a="Limited by ship's draft";break;case 5:e="靠泊",a="Mooring";break;case 6:e="搁浅",a="Stranded";break;case 7:e="捕捞作业",a="Engaged in fishing";break;case 8:e="靠帆船提供动力",a="Sailing";break;default:e="未定义",a="Undefined"}return{labelCn:e,labelEn:a}}}class L extends V{constructor(e,a){super();A(this,"clientId");A(this,"clientSecret");A(this,"token");this.clientId=e,this.clientSecret=a}async authToken(e={}){const a="https://svc.data.myvessel.cn/ada/oauth/token",n={searchParams:{client_id:this.clientId,client_secret:this.clientSecret,grant_type:"client_credentials"}},o=await y.post(a,n).json();b.info("[%s] fetch access token from: %s - %j",e.requestId,a,o),o.error||(this.token={accessToken:o.access_token,tokenType:o.token_type,expiresIn:o.expires_in,scope:o.scope,jti:o.jti,issuedAt:g().utc().format()})}async realTimePosition(e,a={}){var d,c,l;(!this.token||g().diff(g(this.token.issuedAt),"seconds")>((d=this.token)==null?void 0:d.expiresIn)-300)&&await this.authToken(a);const n="https://svc.data.myvessel.cn/sdc/v1/vessels/status/location/unit",o={headers:{Authorization:`${(c=this.token)==null?void 0:c.tokenType} ${(l=this.token)==null?void 0:l.accessToken}`},searchParams:{mmsi:e}};b.info("[%s] fetch realtime position from: %s - %j",a.requestId,n,o);const s=await y.get(n,o).json();if(s.code)return b.warn("[%s] fetch realtime position failed: %j",a.requestId,n,{message:s.message,status:s.status,code:s.code}),s;const t=s.data;for(const u in t)!isNaN(t[u])&&Number(t[u])!==1/0&&(t[u]=Number(t[u]));const r=g(`${t.postime} +08:00`,"YYYY-MM-DD HH:mm:ss +08:00");return{mmsi:t.mmsi,name:t.vesselName,imo:t.imo,callSign:t.callsign,lat:t.lat,lng:t.lon,length:t.length,width:t.width,draught:t.currDraught,sog:t.sog,cog:t.cog,hdg:t.hdg,rot:t.rot,eta:t.eta,destination:t.dest,positionTime:r.unix(),status:t.status,labelCn:t.statusNameCn,labelEn:t.statusNameEn,method:"position",vendor:"myVessel",utc:r.utc().format()}}async trajectory(e,a,n,o,s=!0,t={}){(!this.token||g().diff(g(this.token.issuedAt),"seconds")>this.token.expiresIn-300)&&await this.authToken(t);const r=await this.realTimePosition(e,t),i=g(a),d=g(n),c=[];for(;d.diff(i,"day",!0)>30;)await this.trajectoryIn30Day(e,i,i.clone().add(30,"day"),r,o,c,t),i.add(30,"day");return await this.trajectoryIn30Day(e,i,d,r,o,c,t),c}async trajectoryIn30Day(e,a,n,o,s,t,r={}){var h,I,Y,k,w;const i="https://svc.data.myvessel.cn/sdc/v1/vessels/status/track",d={headers:{Authorization:`${(h=this.token)==null?void 0:h.tokenType} ${(I=this.token)==null?void 0:I.accessToken}`},json:{mmsi:e,startTime:a.utcOffset(8).format("YYYY-MM-DD HH:mm:ss"),endTime:n.utcOffset(8).format("YYYY-MM-DD HH:mm:ss")}};b.info("[%s] fetch trajectory from: %s - %j",r.requestId,i,d);const c=await y.post(i,d).json();if(c.code)return b.warn("[%s] fetch trajectory failed: %j",r.requestId,i,{message:c.message,status:c.status,code:c.code}),c;let l=-1;const u=g(`${(k=(Y=c.data)==null?void 0:Y[0])==null?void 0:k.postime} +08:00`,"YYYY-MM-DD HH:mm:ss +08:00");return(w=c.data)==null||w.forEach(m=>{for(const T in m)!isNaN(m[T])&&Number(m[T])!==1/0&&(m[T]=Number(m[T]));const v=g(`${m.postime} +08:00`,"YYYY-MM-DD HH:mm:ss +08:00"),N=m.eta?g(`${m.eta} +08:00`,"YYYY-MM-DD HH:mm:ss +08:00"):void 0,f=m.status,{labelCn:q,labelEn:R}=this.parseStatus(f),P={mmsi:m.mmsi,imo:o==null?void 0:o.imo,lat:m.lat,lng:m.lon,sog:m.sog,cog:m.cog,hdg:m.hdg,draught:m.draught,status:f,eta:N==null?void 0:N.unix(),destination:m.dest,positionTime:v.unix(),labelCn:q,labelEn:R,method:"trajectory",vendor:"myVessel",utc:v.utc().format()},x=Math.floor(v.diff(u,"minute",!0)/(s||1));x!==l&&(l=x,t.push(P))}),t}}class z extends V{constructor(e){super();A(this,"token");this.token=e}async realTimePosition(e,a={}){const n="https://api.hifleet.com/position/position/get/token",o={searchParams:{mmsi:e,usertoken:this.token}},s=await y.post(n,o).json();b.info("[%s] fetch realtime position from: %s - %j",a.requestId,n,o);const t=s==null?void 0:s.list;if(!t)return b.warn("[%s] fetch realtime position failed: %j",a.requestId,n,s),s;for(const u in t)!isNaN(t[u])&&Number(t[u])!==1/0&&(t[u]=Number(t[u]));t.status=t.sp>3?0:1;const r=t.status,{labelCn:i,labelEn:d}=this.parseStatus(r),c=g(`${t.ti} +08:00`,"YYYY-MM-DD HH:mm:ss +08:00");return{mmsi:t.m,name:t.n,imo:t.imonumber,callSign:t.callsign,lat:Math.round(t.la/60*1e5)/1e5,lng:Math.round(t.lo/60*1e5)/1e5,length:t.l,width:t.w,draught:t.draught,sog:t.sp,cog:t.co,hdg:t.h,rot:isNaN(t.rot)?0:t.rot,eta:/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/.test(t.eta)?g(`${t.eta} +08:00`,"YYYY-MM-DD HH:mm:ss +08:00").unix():void 0,destination:t.destination,positionTime:c.unix(),utc:c.utc().format(),status:r,labelCn:i,labelEn:d,method:"position",vendor:"hifleet"}}async search(e,a={}){let n="https://www.hifleet.com/hifleetapi/searchVesselOL.do";const o={searchParams:{keyword:e},headers:{Referer:"https://www.hifleet.com",Origin:"https://www.hifleet.com",Host:"www.hifleet.com"}};let s=await y.post(n,o).json();b.info("[%s] fetch vessel props from: %s - %j",a.requestId,n,o),s instanceof Array&&(s=s[0]);for(const r in s)!isNaN(s[r])&&Number(s[r])!==1/0&&(s[r]=Number(s[r]));const t={mmsi:s.m,name:s.n,imo:s.i,callSign:s.c,length:s.l,breadth:s.b,draught:s.dr};return n="https://www.hifleet.com/hifleetapi/sameShipSearch.do",s=await y.post(n,o).json(),b.info("[%s] fetch vessel dead weight from: %s - %j",a.requestId,n,o),s instanceof Array&&(s=s[0]),s&&(t.deadweight=Number(s.dwt)),t}async trajectory(e,a,n,o,s=!0,t={}){var m,v,N;const r=await this.realTimePosition(e,t);let i=g(a);const d=g(n),c=g();if(s){let f=d.diff(i,"d",!0);f<0?i=d.clone().subtract(40,"d"):f<30?i.subtract(10,"d"):f<60?i.subtract(5,"d"):i=d.clone().subtract(80,"d"),f=c.diff(d,"d",!0),d.add(f>10?240:f*24,"h")}const l={searchParams:{endtime:d.utcOffset("+8:00").format("YYYY-MM-DD HH:mm:ss"),starttime:i.utcOffset("+8:00").format("YYYY-MM-DD HH:mm:ss"),mmsi:e,usertoken:this.token}},u="https://api.hifleet.com/position/trajectory/nocompressed/withstatic/token",h=await y.get(u,l).json();b.info("[%s] fetch trajectory from: %s - %j",t.requestId,u,l);let I;h&&(I=((v=(m=h.ships)==null?void 0:m.offors)==null?void 0:v.ship)||[],I.length||b.warn("[%s] fetch trajectory failed: %j",t.requestId,h));const Y=[];let k=-1;const w=g(`${(N=I==null?void 0:I[0])==null?void 0:N.ti} +08:00`,"YYYY-MM-DD HH:mm:ss +08:00");for(const f of I){for(const _ in f)!isNaN(f[_])&&Number(f[_])!==1/0&&(f[_]=Number(f[_]));const q=g(`${f.ti} +08:00`,"YYYY-MM-DD HH:mm:ss +08:00");f.status=f.sp>4?0:1;const{labelEn:R,labelCn:P}=this.parseStatus(f.status),x={mmsi:f.m,name:f.n,imo:r==null?void 0:r.imo,lat:f.la,lng:f.lo,draught:f.draught,sog:f.sp,cog:f.co,hdg:f.hdg,positionTime:q.unix(),utc:q.utc().format(),status:f.status,labelCn:P,labelEn:R,method:"trajectory",vendor:"hifleet"},T=Math.floor(q.diff(w,"minute",!0)/(o||1));T!==k&&(k=T,Y.push(x))}return Y}}class F extends V{constructor(e){super();A(this,"token");this.token=e}async realTimePosition(e,a={}){const n={searchParams:{id:e,k:this.token,enc:1}},o="https://api.shipxy.com/apicall/GetSingleShip",s=await y.get(o,n).json();if(b.info("[%s] fetch realtime position from: %s - %j",a.requestId,o,n),(s==null?void 0:s.status)!==0)return s;const t=s.data[0];for(const l in t)!isNaN(t[l])&&Number(t[l])!==1/0&&(t[l]=Number(t[l]));const{labelCn:r,labelEn:i}=await this.parseStatus(t.navistat),d=g.unix(t.lasttime);return{mmsi:t.ShipID,name:t.name,imo:t.imo,callSign:t.callsign,lat:Math.round(t.lat/1e6*1e5)/1e5,lng:Math.round(t.lon/1e6*1e5)/1e5,length:Math.round(t.length/10*100)/100,width:Math.round(t.width/10*100)/100,draught:Math.round(t.draught/1e3*100)/100,sog:Math.round(t.sog*3600/1e3/1852*100)/100,cog:Math.round(t.cog/100*100)/100,hdg:Math.round(t.hdg/100*100)/100,rot:Math.round(t.rot/100*100)/100,positionTime:t.lasttime,utc:d.utc().format(),status:t.navistat,labelEn:i,labelCn:r,method:"position",vendor:"shipxy"}}async trajectory(e,a,n,o,s=!0,t={}){var w;const r=await this.realTimePosition(e,t),i=g(a),d=g(n),c="https://api.shipxy.com/apicall/GetShipTrack",l={searchParams:{id:e,k:this.token,enc:1,cut:0,btm:i.unix(),etm:d.unix()}},u=await y.get(c,l).json();if(b.info("[%s] fetch trajectory from: %s - %j",t.requestId,c,l),(u==null?void 0:u.status)!==0)return u;const h=u==null?void 0:u.points,I=[],Y=g.unix((w=h[0])==null?void 0:w.utc);let k=-1;for(const m of h){const v=g.unix(m.utc),N={imo:r==null?void 0:r.imo,mmsi:e,sog:Math.round(m.sog*3600/1e3/1852*100)/100,cog:Math.round(m.cog/100*100)/100,lat:Math.round(m.lat/1e6*1e5)/1e5,lng:Math.round(m.lon/1e6*1e5)/1e5,positionTime:v.unix(),utc:v.utc().format(),method:"trajectory",vendor:"shipxy"},f=Math.floor(v.diff(Y,"minute",!0)/(o||1));f!==k&&(k=f,I.push(N))}return I}}class G extends V{constructor(e){super();A(this,"token");this.token=e}async getShipId(e,a={}){const n={headers:{appKey:this.token},json:{mmsiList:e}},o="https://api3.myships.com/sp/ships/getShipIdByMMSI",s=await y.post(o,n).json();return b.info("[%s] fetch ship id from: %s - %j",a.requestId,o,n),s.code!=="0"?s:s.data[0].shipId}async getShipInfo(e,a={}){const n={headers:{appKey:this.token},json:{shipId:e}},o="https://api3.myships.com/sp/ships/aissta",s=await y.post(o,n).json();if(b.info("[%s] fetch ship info from: %s - %j",a.requestId,o,n),s.code!=="0")return s;const t=s.data;let r=t.imo;return e==="407170"&&(r="9198379",b.warn("[%s] ship(%s) imo error: %s, should be %s",a.requestId,e,t.imo,r)),{mmsi:t.mmsi,name:t.shipnameEn,imo:r,callSign:t.callSign,length:t.length,width:t.breadth,draught:(t.draught||100)/10}}async realTimePosition(e,a={}){const n=await this.getShipId(e,a),o=await this.getShipInfo(n,a),s={headers:{appKey:this.token},json:{shipId:n}},t="https://api3.myships.com/sp/ships/position/latest",r=await y.post(t,s).json();b.info("[%s] fetch realtime position from: %s - %j",a.requestId,t,s);const i=r.data[0];for(const h in i)!isNaN(i[h])&&Number(i[h])!==1/0&&(i[h]=Number(i[h]));const{labelCn:d,labelEn:c}=await this.parseStatus(i.aisNavStatus),l=g.unix(i.posTime);return{...o,mmsi:e,lat:Math.round(i.lat/1e4/60*1e5)/1e5,lng:Math.round(i.lon/1e4/60*1e5)/1e5,sog:Math.round(i.sog/10*100)/100,cog:Math.round(i.cog/10*100)/100,hdg:Math.round(i.heading*100)/100,rot:Math.round(i.rot*100)/100,positionTime:i.posTime,utc:l.utc().format(),status:i.aisNavStatus,labelEn:c,labelCn:d,method:"position",vendor:"myship"}}async trajectory(e,a,n,o,s=!0,t={}){const r=g(a),i=g(n),d=await this.getShipId(e),c=await this.getShipInfo(d),l=[];for(;i.diff(r,"day",!0)>30;)await this.trajectoryIn30Day(d,r.unix(),r.add(30,"day").unix(),c,e,o,l);return await this.trajectoryIn30Day(d,r.unix(),i.unix(),c,e,o,l),l}async trajectoryIn30Day(e,a,n,o,s,t,r,i={}){var Y;const d={headers:{appKey:this.token},json:{shipId:e,startTime:a,endTime:n}},c="https://api3.myships.com/sp/ships/position/history",l=await y.post(c,d).json();if(b.info("[%s] fetch trajectory from: %s - %j",i.requestId,c,d),l.code!=="0")return b.warn("[%s] invoke myship trajectory failed: %j",i.requestId,l),l;const u=l.data;for(const k in u)!isNaN(u[k])&&Number(u[k])!==1/0&&(u[k]=Number(u[k]));const h=g.unix((Y=u[0])==null?void 0:Y.posTime);let I=-1;for(const k of u){const w=g.unix(k.posTime),m={imo:o==null?void 0:o.imo,mmsi:s,lat:Math.round(k.lat/1e4/60*1e5)/1e5,lng:Math.round(k.lon/1e4/60*1e5)/1e5,sog:Math.round(k.sog/10*100)/100,cog:Math.round(k.cog/10*100)/100,hdg:Math.round(k.heading*100)/100,rot:Math.round(k.rot*100)/100,positionTime:w.unix(),utc:w.utc().format(),method:"trajectory",vendor:"myship"},v=Math.floor(w.diff(h,"minute",!0)/(t||1));v!==I&&(I=v,r.push(m))}return r}}const O=E.getLogger("vessel");var C=(S=>(S.NOTICE="NOTICE",S.WARN="WARN",S.HEAVY="HEAVY",S.SEVERE="SEVERE",S.ERROR="ERROR",S.FATAL="FATAL",S))(C||{});class K{parsePrinciple(p,e={}){var t,r,i;O.info("[%s] parse rule: %s",e.requestId,p);const a=new RegExp("(?<=\\[)(.+)(?=])","g"),n=p.match(a)?(t=p.match(a))==null?void 0:t[0]:void 0,o=n==null?void 0:n.split(";");if(!o)return;const s={};for(let d=0;d<(o==null?void 0:o.length);d++){const c=(i=(r=o[d].match(a))==null?void 0:r[0])==null?void 0:i.split("],");if(d===0&&!c)s.scope=o[0];else if(c)for(let l=0,u=c.length;l<u;l++){const h=this.parseRule(c[l]);h&&(s[h.level]?h.key?s[h.level][h==null?void 0:h.key]=h:s[h.level]=h:h.key?s[h.level]={[h==null?void 0:h.key]:h}:s[h.level]=h)}}return s}parseRule(p,e={}){var s;O.info("[%s] parse rule: %s",e.requestId,p),p=p.startsWith("[")?p:`[${p}`,p=p.endsWith("]")?p:`${p}]`;const a=new RegExp("(?<=\\[)(.+?)(?=])","g"),n=(s=p==null?void 0:p.match(a))==null?void 0:s[0],o=n==null?void 0:n.split(",");if(o)return{operator:o[0],number:Number.isNaN(Number(o[1]))?o[1]:Number(o[1]),level:o[2],time:Number(o[3]),key:o[4]}}checkWeather(p,e,a={}){var h,I,Y,k,w,m,v,N,f,q,R,P,x,T,_;let n=0,o=0,s=0,t=0;const r=Math.round(((I=(h=e==null?void 0:e.SEVERE)==null?void 0:h.sigWave)==null?void 0:I.number)*1.6*100)/100,i=(k=(Y=e==null?void 0:e.SEVERE)==null?void 0:Y.sigWave)==null?void 0:k.number,d=(m=(w=e==null?void 0:e.HEAVY)==null?void 0:w.sigWave)==null?void 0:m.number,c=Math.round((((N=(v=e==null?void 0:e.SEVERE)==null?void 0:v.wind)==null?void 0:N.number)+2)*100)/100,l=(q=(f=e==null?void 0:e.SEVERE)==null?void 0:f.wind)==null?void 0:q.number,u=(P=(R=e==null?void 0:e.HEAVY)==null?void 0:R.wind)==null?void 0:P.number;for(let $=0;$<(p==null?void 0:p.length);$++){const M=p[$],H=(T=(x=M==null?void 0:M.meteo)==null?void 0:x.wave)==null?void 0:T.sig,D=(_=M==null?void 0:M.meteo)==null?void 0:_.wind,W=$?g(M.eta).diff(g(p[$-1].eta),"hour",!0):0;t=W>t?W:t,O.info("[%s] check sig.wave: %j",a.requestId,{...H,dgThd4Wv:r,svThd4Wv:i,hvThd4Wv:d}),(H==null?void 0:H.height)>=r?M.isDangerous=!0:(H==null?void 0:H.height)>=i?M.isSevere=!0:(H==null?void 0:H.height)>=d&&(M.isHeavy=!0),O.info("[%s] check wind: %j",a.requestId,{...D,dgThd4Wd:c,svThd4Wd:l,hvThd4Wd:u}),(D==null?void 0:D.scale)>=c?(M.isDangerous=!0,delete M.isSevere,delete M.isHeavy):(D==null?void 0:D.scale)>l?(M.isDangerous||(M.isSevere=!0),delete M.isHeavy):(D==null?void 0:D.scale)===u&&!M.isDangerous&&!M.isSevere&&(M.isHeavy=!0),n+=M.isDangerous?W:0,o+=M.isSevere?W:0,s+=M.isHeavy?W:0}return n=Math.round(n*100)/100,o=Math.round(o*100)/100,s=Math.round(s*100)/100,t=Math.round(t),{sample:p,dangerous:n,severe:o,heavy:s,step:t<3?3:t,wind:{dgThd4Wd:c,svThd4Wd:l,hvThd4Wd:u},sig:{dgThd4Wv:r,svThd4Wv:i,hvThd4Wv:d}}}}const B=new K;j.AISImpl=V,j.AlertHelper=K,j.AlertLevel=C,j.HifleetImpl=z,j.MyShipImpl=G,j.MyVesselImpl=L,j.ShipxyImpl=F,j.alertHelper=B,Object.defineProperty(j,Symbol.toStringTag,{value:"Module"})});
1
+ (function(j,w){typeof exports=="object"&&typeof module<"u"?w(exports,require("got"),require("@log4js-node/log4js-api"),require("moment"),require("@idm-plugin/geo"),require("@idm-plugin/meteo")):typeof define=="function"&&define.amd?define(["exports","got","@log4js-node/log4js-api","moment","@idm-plugin/geo","@idm-plugin/meteo"],w):(j=typeof globalThis<"u"?globalThis:j||self,w(j["idm-plugin-rabbitmq"]={},j.got,j["@log4js-node/log4js-api"],j.moment,j["@idm-plugin/geo"],j["@idm-plugin/meteo"]))})(this,function(j,w,R,y,x,st){"use strict";var dt=Object.defineProperty;var ut=(j,w,R)=>w in j?dt(j,w,{enumerable:!0,configurable:!0,writable:!0,value:R}):j[w]=R;var _=(j,w,R)=>(ut(j,typeof w!="symbol"?w+"":w,R),R);const D=R.getLogger("vessel");class Z{parseStatus(a){let t,i;switch(a){case 0:t="在航(主机推动)",i="The engine is in use";break;case 1:t="锚泊",i="Anchored";break;case 2:t="失控",i="Not operated";break;case 3:t="操纵受限",i="Limited airworthiness";break;case 4:t="吃水受限",i="Limited by ship's draft";break;case 5:t="靠泊",i="Mooring";break;case 6:t="搁浅",i="Stranded";break;case 7:t="捕捞作业",i="Engaged in fishing";break;case 8:t="靠帆船提供动力",i="Sailing";break;default:t="未定义",i="Undefined"}return{labelCn:t,labelEn:i}}}class at extends Z{constructor(t,i){super();_(this,"clientId");_(this,"clientSecret");_(this,"token");this.clientId=t,this.clientSecret=i}async authToken(t={}){const i="https://svc.data.myvessel.cn/ada/oauth/token",o={searchParams:{client_id:this.clientId,client_secret:this.clientSecret,grant_type:"client_credentials"}},n=await w.post(i,o).json();D.info("[%s] fetch access token from: %s - %j",t.requestId,i,n),n.error||(this.token={accessToken:n.access_token,tokenType:n.token_type,expiresIn:n.expires_in,scope:n.scope,jti:n.jti,issuedAt:y().utc().format()})}async realTimePosition(t,i={}){var f,m,p;(!this.token||y().diff(y(this.token.issuedAt),"seconds")>((f=this.token)==null?void 0:f.expiresIn)-300)&&await this.authToken(i);const o="https://svc.data.myvessel.cn/sdc/v1/vessels/status/location/unit",n={headers:{Authorization:`${(m=this.token)==null?void 0:m.tokenType} ${(p=this.token)==null?void 0:p.accessToken}`},searchParams:{mmsi:t}};D.info("[%s] fetch realtime position from: %s - %j",i.requestId,o,n);const s=await w.get(o,n).json();if(s.code)return D.warn("[%s] fetch realtime position failed: %j",i.requestId,o,{message:s.message,status:s.status,code:s.code}),s;const e=s.data;for(const l in e)!isNaN(e[l])&&Number(e[l])!==1/0&&(e[l]=Number(e[l]));const c=y(`${e.postime} +08:00`,"YYYY-MM-DD HH:mm:ss +08:00");return{mmsi:e.mmsi,name:e.vesselName,imo:e.imo,callSign:e.callsign,lat:e.lat,lng:e.lon,length:e.length,width:e.width,draught:e.currDraught,sog:e.sog,cog:e.cog,hdg:e.hdg,rot:e.rot,eta:e.eta,destination:e.dest,positionTime:c.unix(),status:e.status,labelCn:e.statusNameCn,labelEn:e.statusNameEn,method:"position",vendor:"myVessel",utc:c.utc().format()}}async trajectory(t,i,o,n,s=!0,e={}){(!this.token||y().diff(y(this.token.issuedAt),"seconds")>this.token.expiresIn-300)&&await this.authToken(e);const c=await this.realTimePosition(t,e),r=y(i),f=y(o),m=[];for(;f.diff(r,"day",!0)>30;)await this.trajectoryIn30Day(t,r,r.clone().add(30,"day"),c,n,m,e),r.add(30,"day");return await this.trajectoryIn30Day(t,r,f,c,n,m,e),m}async trajectoryIn30Day(t,i,o,n,s,e,c={}){var u,b,k,d,g;const r="https://svc.data.myvessel.cn/sdc/v1/vessels/status/track",f={headers:{Authorization:`${(u=this.token)==null?void 0:u.tokenType} ${(b=this.token)==null?void 0:b.accessToken}`},json:{mmsi:t,startTime:i.utcOffset(8).format("YYYY-MM-DD HH:mm:ss"),endTime:o.utcOffset(8).format("YYYY-MM-DD HH:mm:ss")}};D.info("[%s] fetch trajectory from: %s - %j",c.requestId,r,f);const m=await w.post(r,f).json();if(m.code)return D.warn("[%s] fetch trajectory failed: %j",c.requestId,r,{message:m.message,status:m.status,code:m.code}),m;let p=-1;const l=y(`${(d=(k=m.data)==null?void 0:k[0])==null?void 0:d.postime} +08:00`,"YYYY-MM-DD HH:mm:ss +08:00");return(g=m.data)==null||g.forEach(h=>{for(const H in h)!isNaN(h[H])&&Number(h[H])!==1/0&&(h[H]=Number(h[H]));const v=y(`${h.postime} +08:00`,"YYYY-MM-DD HH:mm:ss +08:00"),I=h.eta?y(`${h.eta} +08:00`,"YYYY-MM-DD HH:mm:ss +08:00"):void 0,M=h.status,{labelCn:E,labelEn:q}=this.parseStatus(M),A={mmsi:h.mmsi,imo:n==null?void 0:n.imo,lat:h.lat,lng:h.lon,sog:h.sog,cog:h.cog,hdg:h.hdg,draught:h.draught,status:M,eta:I==null?void 0:I.unix(),destination:h.dest,positionTime:v.unix(),labelCn:E,labelEn:q,method:"trajectory",vendor:"myVessel",utc:v.utc().format()},S=Math.floor(v.diff(l,"minute",!0)/(s||1));S!==p&&(p=S,e.push(A))}),e}}class ot extends Z{constructor(t){super();_(this,"token");this.token=t}async realTimePosition(t,i={}){const o="https://api.hifleet.com/position/position/get/token",n={searchParams:{mmsi:t,usertoken:this.token}},s=await w.post(o,n).json();D.info("[%s] fetch realtime position from: %s - %j",i.requestId,o,n);const e=s==null?void 0:s.list;if(!e)return D.warn("[%s] fetch realtime position failed: %j",i.requestId,o,s),s;for(const l in e)!isNaN(e[l])&&Number(e[l])!==1/0&&(e[l]=Number(e[l]));e.status=e.sp>3?0:1;const c=e.status,{labelCn:r,labelEn:f}=this.parseStatus(c),m=y(`${e.ti} +08:00`,"YYYY-MM-DD HH:mm:ss +08:00");return{mmsi:e.m,name:e.n,imo:e.imonumber,callSign:e.callsign,lat:Math.round(e.la/60*1e5)/1e5,lng:Math.round(e.lo/60*1e5)/1e5,length:e.l,width:e.w,draught:e.draught,sog:e.sp,cog:e.co,hdg:e.h,rot:isNaN(e.rot)?0:e.rot,eta:/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/.test(e.eta)?y(`${e.eta} +08:00`,"YYYY-MM-DD HH:mm:ss +08:00").unix():void 0,destination:e.destination,positionTime:m.unix(),utc:m.utc().format(),status:c,labelCn:r,labelEn:f,method:"position",vendor:"hifleet"}}async search(t,i={}){let o="https://www.hifleet.com/hifleetapi/searchVesselOL.do";const n={searchParams:{keyword:t},headers:{Referer:"https://www.hifleet.com",Origin:"https://www.hifleet.com",Host:"www.hifleet.com"}};let s=await w.post(o,n).json();D.info("[%s] fetch vessel props from: %s - %j",i.requestId,o,n),s instanceof Array&&(s=s[0]);for(const c in s)!isNaN(s[c])&&Number(s[c])!==1/0&&(s[c]=Number(s[c]));const e={mmsi:s.m,name:s.n,imo:s.i,callSign:s.c,length:s.l,breadth:s.b,draught:s.dr};return o="https://www.hifleet.com/hifleetapi/sameShipSearch.do",s=await w.post(o,n).json(),D.info("[%s] fetch vessel dead weight from: %s - %j",i.requestId,o,n),s instanceof Array&&(s=s[0]),s&&(e.deadweight=Number(s.dwt)),e}async trajectory(t,i,o,n,s=!0,e={}){var h,v,I;const c=await this.realTimePosition(t,e);let r=y(i);const f=y(o),m=y();if(s){let M=f.diff(r,"d",!0);M<0?r=f.clone().subtract(40,"d"):M<30?r.subtract(10,"d"):M<60?r.subtract(5,"d"):r=f.clone().subtract(80,"d"),M=m.diff(f,"d",!0),f.add(M>10?240:M*24,"h")}const p={searchParams:{endtime:f.utcOffset("+8:00").format("YYYY-MM-DD HH:mm:ss"),starttime:r.utcOffset("+8:00").format("YYYY-MM-DD HH:mm:ss"),mmsi:t,usertoken:this.token}},l="https://api.hifleet.com/position/trajectory/nocompressed/withstatic/token",u=await w.get(l,p).json();D.info("[%s] fetch trajectory from: %s - %j",e.requestId,l,p);let b;u&&(b=((v=(h=u.ships)==null?void 0:h.offors)==null?void 0:v.ship)||[],b.length||D.warn("[%s] fetch trajectory failed: %j",e.requestId,u));const k=[];let d=-1;const g=y(`${(I=b==null?void 0:b[0])==null?void 0:I.ti} +08:00`,"YYYY-MM-DD HH:mm:ss +08:00");for(const M of b){for(const O in M)!isNaN(M[O])&&Number(M[O])!==1/0&&(M[O]=Number(M[O]));const E=y(`${M.ti} +08:00`,"YYYY-MM-DD HH:mm:ss +08:00");M.status=M.sp>4?0:1;const{labelEn:q,labelCn:A}=this.parseStatus(M.status),S={mmsi:M.m,name:M.n,imo:c==null?void 0:c.imo,lat:M.la,lng:M.lo,draught:M.draught,sog:M.sp,cog:M.co,hdg:M.hdg,positionTime:E.unix(),utc:E.utc().format(),status:M.status,labelCn:A,labelEn:q,method:"trajectory",vendor:"hifleet"},H=Math.floor(E.diff(g,"minute",!0)/(n||1));H!==d&&(d=H,k.push(S))}return k}}class nt extends Z{constructor(t){super();_(this,"token");this.token=t}async realTimePosition(t,i={}){const o={searchParams:{id:t,k:this.token,enc:1}},n="https://api.shipxy.com/apicall/GetSingleShip",s=await w.get(n,o).json();if(D.info("[%s] fetch realtime position from: %s - %j",i.requestId,n,o),(s==null?void 0:s.status)!==0)return s;const e=s.data[0];for(const p in e)!isNaN(e[p])&&Number(e[p])!==1/0&&(e[p]=Number(e[p]));const{labelCn:c,labelEn:r}=await this.parseStatus(e.navistat),f=y.unix(e.lasttime);return{mmsi:e.ShipID,name:e.name,imo:e.imo,callSign:e.callsign,lat:Math.round(e.lat/1e6*1e5)/1e5,lng:Math.round(e.lon/1e6*1e5)/1e5,length:Math.round(e.length/10*100)/100,width:Math.round(e.width/10*100)/100,draught:Math.round(e.draught/1e3*100)/100,sog:Math.round(e.sog*3600/1e3/1852*100)/100,cog:Math.round(e.cog/100*100)/100,hdg:Math.round(e.hdg/100*100)/100,rot:Math.round(e.rot/100*100)/100,positionTime:e.lasttime,utc:f.utc().format(),status:e.navistat,labelEn:r,labelCn:c,method:"position",vendor:"shipxy"}}async trajectory(t,i,o,n,s=!0,e={}){var g;const c=await this.realTimePosition(t,e),r=y(i),f=y(o),m="https://api.shipxy.com/apicall/GetShipTrack",p={searchParams:{id:t,k:this.token,enc:1,cut:0,btm:r.unix(),etm:f.unix()}},l=await w.get(m,p).json();if(D.info("[%s] fetch trajectory from: %s - %j",e.requestId,m,p),(l==null?void 0:l.status)!==0)return l;const u=l==null?void 0:l.points,b=[],k=y.unix((g=u[0])==null?void 0:g.utc);let d=-1;for(const h of u){const v=y.unix(h.utc),I={imo:c==null?void 0:c.imo,mmsi:t,sog:Math.round(h.sog*3600/1e3/1852*100)/100,cog:Math.round(h.cog/100*100)/100,lat:Math.round(h.lat/1e6*1e5)/1e5,lng:Math.round(h.lon/1e6*1e5)/1e5,positionTime:v.unix(),utc:v.utc().format(),method:"trajectory",vendor:"shipxy"},M=Math.floor(v.diff(k,"minute",!0)/(n||1));M!==d&&(d=M,b.push(I))}return b}}class it extends Z{constructor(t){super();_(this,"token");this.token=t}async getShipId(t,i={}){const o={headers:{appKey:this.token},json:{mmsiList:t}},n="https://api3.myships.com/sp/ships/getShipIdByMMSI",s=await w.post(n,o).json();return D.info("[%s] fetch ship id from: %s - %j",i.requestId,n,o),s.code!=="0"?s:s.data[0].shipId}async getShipInfo(t,i={}){const o={headers:{appKey:this.token},json:{shipId:t}},n="https://api3.myships.com/sp/ships/aissta",s=await w.post(n,o).json();if(D.info("[%s] fetch ship info from: %s - %j",i.requestId,n,o),s.code!=="0")return s;const e=s.data;let c=e.imo;return t==="407170"&&(c="9198379",D.warn("[%s] ship(%s) imo error: %s, should be %s",i.requestId,t,e.imo,c)),{mmsi:e.mmsi,name:e.shipnameEn,imo:c,callSign:e.callSign,length:e.length,width:e.breadth,draught:(e.draught||100)/10}}async realTimePosition(t,i={}){const o=await this.getShipId(t,i),n=await this.getShipInfo(o,i),s={headers:{appKey:this.token},json:{shipId:o}},e="https://api3.myships.com/sp/ships/position/latest",c=await w.post(e,s).json();D.info("[%s] fetch realtime position from: %s - %j",i.requestId,e,s);const r=c.data[0];for(const u in r)!isNaN(r[u])&&Number(r[u])!==1/0&&(r[u]=Number(r[u]));const{labelCn:f,labelEn:m}=await this.parseStatus(r.aisNavStatus),p=y.unix(r.posTime);return{...n,mmsi:t,lat:Math.round(r.lat/1e4/60*1e5)/1e5,lng:Math.round(r.lon/1e4/60*1e5)/1e5,sog:Math.round(r.sog/10*100)/100,cog:Math.round(r.cog/10*100)/100,hdg:Math.round(r.heading*100)/100,rot:Math.round(r.rot*100)/100,positionTime:r.posTime,utc:p.utc().format(),status:r.aisNavStatus,labelEn:m,labelCn:f,method:"position",vendor:"myship"}}async trajectory(t,i,o,n,s=!0,e={}){const c=y(i),r=y(o),f=await this.getShipId(t),m=await this.getShipInfo(f),p=[];for(;r.diff(c,"day",!0)>30;)await this.trajectoryIn30Day(f,c.unix(),c.add(30,"day").unix(),m,t,n,p);return await this.trajectoryIn30Day(f,c.unix(),r.unix(),m,t,n,p),p}async trajectoryIn30Day(t,i,o,n,s,e,c,r={}){var k;const f={headers:{appKey:this.token},json:{shipId:t,startTime:i,endTime:o}},m="https://api3.myships.com/sp/ships/position/history",p=await w.post(m,f).json();if(D.info("[%s] fetch trajectory from: %s - %j",r.requestId,m,f),p.code!=="0")return D.warn("[%s] invoke myship trajectory failed: %j",r.requestId,p),p;const l=p.data;for(const d in l)!isNaN(l[d])&&Number(l[d])!==1/0&&(l[d]=Number(l[d]));const u=y.unix((k=l[0])==null?void 0:k.posTime);let b=-1;for(const d of l){const g=y.unix(d.posTime),h={imo:n==null?void 0:n.imo,mmsi:s,lat:Math.round(d.lat/1e4/60*1e5)/1e5,lng:Math.round(d.lon/1e4/60*1e5)/1e5,sog:Math.round(d.sog/10*100)/100,cog:Math.round(d.cog/10*100)/100,hdg:Math.round(d.heading*100)/100,rot:Math.round(d.rot*100)/100,positionTime:g.unix(),utc:g.utc().format(),method:"trajectory",vendor:"myship"},v=Math.floor(g.diff(u,"minute",!0)/(e||1));v!==b&&(b=v,c.push(h))}return c}}const K=R.getLogger("vessel");var z=(Y=>(Y.NOTICE="NOTICE",Y.WARN="WARN",Y.HEAVY="HEAVY",Y.SEVERE="SEVERE",Y.ERROR="ERROR",Y.FATAL="FATAL",Y))(z||{});class G{parsePrinciple(a,t={}){var e,c,r;K.info("[%s] parse rule: %s",t.requestId,a);const i=new RegExp("(?<=\\[)(.+)(?=])","g"),o=a.match(i)?(e=a.match(i))==null?void 0:e[0]:void 0,n=o==null?void 0:o.split(";");if(!n)return;const s={};for(let f=0;f<(n==null?void 0:n.length);f++){const m=(r=(c=n[f].match(i))==null?void 0:c[0])==null?void 0:r.split("],");if(f===0&&!m)s.scope=n[0];else if(m)for(let p=0,l=m.length;p<l;p++){const u=this.parseRule(m[p]);u&&(s[u.level]?u.key?s[u.level][u==null?void 0:u.key]=u:s[u.level]=u:u.key?s[u.level]={[u==null?void 0:u.key]:u}:s[u.level]=u)}}return s}parseRule(a,t={}){var s;K.info("[%s] parse rule: %s",t.requestId,a),a=a.startsWith("[")?a:`[${a}`,a=a.endsWith("]")?a:`${a}]`;const i=new RegExp("(?<=\\[)(.+?)(?=])","g"),o=(s=a==null?void 0:a.match(i))==null?void 0:s[0],n=o==null?void 0:o.split(",");if(n)return{operator:n[0],number:Number.isNaN(Number(n[1]))?n[1]:Number(n[1]),level:n[2],time:Number(n[3]),key:n[4]}}checkWeather(a,t,i={}){var u,b,k,d,g,h,v,I,M,E,q,A,S,H,O;let o=0,n=0,s=0,e=0;const c=Math.round(((b=(u=t==null?void 0:t.SEVERE)==null?void 0:u.sigWave)==null?void 0:b.number)*1.6*100)/100,r=(d=(k=t==null?void 0:t.SEVERE)==null?void 0:k.sigWave)==null?void 0:d.number,f=(h=(g=t==null?void 0:t.HEAVY)==null?void 0:g.sigWave)==null?void 0:h.number,m=Math.round((((I=(v=t==null?void 0:t.SEVERE)==null?void 0:v.wind)==null?void 0:I.number)+2)*100)/100,p=(E=(M=t==null?void 0:t.SEVERE)==null?void 0:M.wind)==null?void 0:E.number,l=(A=(q=t==null?void 0:t.HEAVY)==null?void 0:q.wind)==null?void 0:A.number;for(let W=0;W<(a==null?void 0:a.length);W++){const T=a[W],L=(H=(S=T==null?void 0:T.meteo)==null?void 0:S.wave)==null?void 0:H.sig,C=(O=T==null?void 0:T.meteo)==null?void 0:O.wind,V=W?y(T.eta).diff(y(a[W-1].eta),"hour",!0):0;e=V>e?V:e,K.info("[%s] check sig.wave: %j",i.requestId,{...L,dgThd4Wv:c,svThd4Wv:r,hvThd4Wv:f}),(L==null?void 0:L.height)>=c?T.isDangerous=!0:(L==null?void 0:L.height)>=r?T.isSevere=!0:(L==null?void 0:L.height)>=f&&(T.isHeavy=!0),K.info("[%s] check wind: %j",i.requestId,{...C,dgThd4Wd:m,svThd4Wd:p,hvThd4Wd:l}),(C==null?void 0:C.scale)>=m?(T.isDangerous=!0,delete T.isSevere,delete T.isHeavy):(C==null?void 0:C.scale)>p?(T.isDangerous||(T.isSevere=!0),delete T.isHeavy):(C==null?void 0:C.scale)===l&&!T.isDangerous&&!T.isSevere&&(T.isHeavy=!0),o+=T.isDangerous?V:0,n+=T.isSevere?V:0,s+=T.isHeavy?V:0}return o=Math.round(o*100)/100,n=Math.round(n*100)/100,s=Math.round(s*100)/100,e=Math.round(e),{sample:a,dangerous:o,severe:n,heavy:s,step:e<3?3:e,wind:{dgThd4Wd:m,svThd4Wd:p,hvThd4Wd:l},sig:{dgThd4Wv:c,svThd4Wv:r,hvThd4Wv:f}}}}const rt=new G,B=R.getLogger("vessel");var U=(Y=>(Y.common="common",Y.container="container",Y))(U||{}),J=(Y=>(Y.Ballast="Ballast",Y.Laden="Laden",Y))(J||{}),Q=(Y=>(Y.Cp="CP",Y.Perf="Basis",Y.Instruct="Other",Y))(Q||{});class F{static blockCoefficient(a,t,i,o){let n=Math.round(a/(t*i*o)*100)/100;n=n<.55?.55:n>.85?.85:n;const s=[.55,.6,.65,.7,.75,.8,.85],e=s.map(c=>Math.abs(c-n));return s[e.indexOf(Math.min(...e))]}static froudeNumber(a,t,i=9.8){let o=Math.round(Math.sqrt(a*a/(i*t))*100)/100;return o=o<.05?.05:o>.3?.3:o,o}static amendFactor(a,t,i){const o={.55:[1.7,-1.4,-7.4],.6:[2.2,-2.5,-9.7],.65:[2.6,-3.7,-11.6],.7:[3.1,-5.3,-12.4],.75:[2.4,-10.6,-9.5],.8:[2.6,-13.1,-15.1],.85:[3.1,-18.7,28]};let s={.55:[1.7,-1.4,-7.4],.6:[2.2,-2.5,-9.7],.65:[2.6,-3.7,-11.6],.7:[3.1,-5.3,-12.4],.75:[2.6,-12.5,-13.5],.8:[3,-16.3,-21.6],.85:[3.4,-20.9,31.8]}[a];return i==="Laden"&&(s=o[a]),s[0]+s[1]*t+s[2]*Math.pow(t,2)}static directionFactor(a,t=0){let i;return a>30&&a<=60?i=(1.7-.03*Math.pow(t-4,2))/2:a>60&&a<=150?i=(.9-.06*Math.pow(t-6,2))/2:i=(.4-.03*Math.pow(t-8,2))/2,Math.round(i*1e5)/1e5}static vesselTagFactor(a,t,i,o=0){o=o>5?o-.9*(o-5):o;let n;return i==="container"?n=.7*o+Math.pow(o,6.5)/(22*Math.pow(a,2/3)):t==="Ballast"?n=.7*o+Math.pow(o,6.5)/(2.7*Math.pow(a,2/3)):n=.5*o+Math.pow(o,6.5)/(2.7*Math.pow(a,2/3)),n}static waveHeightFactor(a){return a=a<1.25?1.25:a,a=a>6?a-.9*(a-6):a,Math.round((-.144*Math.pow(a,2)+.178*a)*1e4)/1e4}static assembleProperties(a,t,i,o){var f,m;const n=a.lbp??a.length??a.lengthOverall??198.9642,s=a.draught??8,e=a.breadthMoulded??a.breadth??a.breadthExtreme??32.4572,c=a.deadweight??67035.7773;return{tag:((m=(f=a==null?void 0:a.type)==null?void 0:f.toLowerCase())==null?void 0:m.indexOf("container"))>-1?"container":"common",lbp:n,loadCondition:t,draught:s,breadthMoulded:e,displacement:Math.round((c/1.025+s*e*n*.7)*1e4)/1e4,speed:Math.round((i??14.1382)*1852/3600*1e4)/1e4,bearing:o||90}}static async speedLoseAt(a,t,i,o="",n=2,s=!1,e={}){t.velocity&&!t.noFactor&&s&&(a.speed=x.LngLatHelper.roundPrecision(t.velocity*1852/3600,6));const c=await st.MeteoHelper.queryPointFactor(t.lng,t.lat,i.valueOf(),"wind,wave,current,watertemp",o,e),r=F.weatherFactor(a,c),f=F.currentFactor(a.bearing,c==null?void 0:c.current,n),m={meteo:{...c},wxFactor:r,cFactor:f,speed:t.velocity&&s?t.velocity:Math.round((a.speed*1.943844+r+f)*100)/100,eta:i.utc().format("YYYY-MM-DDTHH:mm[Z]"),etd:i.utc().format("YYYY-MM-DDTHH:mm[Z]")};return delete t.meteo,delete t.wxFactor,delete t.cFactor,delete t.speed,delete t.etd,{...m,...t}}static async speedLoseInHoursStep(a,t,i,o,n,s,e="",c=!1,r={}){t.utc();const f=[],m=[];let p=0,l=0,u,b;for(let k=0;k<s.length-1;k++){let d=s[k];d.distanceFromStart=n+l;const g=s[k+1];if(a.bearing=x.LaneHelper.calculateBearing(d,g,!g.gcToPrevious),d.bearing=a.bearing,d.suspend&&c){d.eta=d.eta||t.format("YYYY-MM-DDTHH:mm[Z]"),d.elapsed=d.elapsed??0;const I=d.suspend-d.elapsed;if(o-p>I)o=o-p-I,t.add(I,"hour"),d.elapsed=d.suspend;else{const M=o-p;d.elapsed+=M,t.add(M,"hour"),o=0}if(B.info(`[%s] suspend ${d.elapsed} hours at %j, and remain ${o} hours need to go...`,r.requestId,d),o===0)return d.distanceFromPrevious=l,{etd:t,from:b||d,to:d,next:s.filter(M=>M),wps:f,days:m}}d=await F.speedLoseAt(a,d,t,e,0,c,r),b=b||d,d.important&&f.push(d),t.isSameOrAfter(i)&&(m.push(d),i.add(24,"hour"));const h=x.LaneHelper.calculateDistance(d,g,!g.gcToPrevious);let v=Math.ceil(h/b.speed*1e4)/1e4;if(p+v<o){if(p+=v,t.add(v,"hour"),delete s[k],B.info(`[%s] go to %j from %j with ${h}nm, and cost ${v} hours`,r.requestId,{lat:g.lat,lng:g.lng},{lat:b.lat,lng:b.lng,etd:b.etd}),l+=h,s.filter(I=>I).length<=1){u=g,u.eta=t.format("YYYY-MM-DDTHH:mm[Z]"),u.distanceFromPrevious=h,u.distanceFromStart=n+l,f.push(u),delete s[k+1];break}}else{v=o-p,t.add(v,"hour");const I=x.LngLatHelper.roundPrecision(b.speed*v,4);u=x.LaneHelper.calculateCoordinate(d,a.bearing,I,"nauticalmiles",!g.gcToPrevious),u.eta=t.format("YYYY-MM-DDTHH:mm[Z]"),s[k]=u,B.info(`[%s] go to %j from %j with ${I}nm, and cost ${v} hours`,r.requestId,{lat:u.lat,lng:u.lng},{lat:d.lat,lng:d.lng,etd:d.etd}),l+=I,u.distanceFromPrevious=l,u.distanceFromStart=n+l;break}}return{etd:t,from:b,to:u,next:s.filter(k=>k),wps:f,days:m}}static currentFactor(a,t,i=0){const o=(a-(t==null?void 0:t.degree)||0)/180*Math.PI;if(Math.abs(o)===Math.PI/2)return 0;let n=((t==null?void 0:t.kts)||0)*Math.cos(o);return i&2?n=Math.ceil(n*100)/100:i&1?n=Math.floor(n*100)/100:n=Math.round(n*100)/100,Math.abs(n)>5?0:n}static weatherFactor(a,t){var m,p,l,u,b;B.debug("calculate weather factor via: %j",{...a,...t});const i=F.blockCoefficient(a.displacement,a.lbp,a.breadthMoulded,a.draught),o=F.froudeNumber(a.speed,a.lbp),n=F.amendFactor(i,o,a.loadCondition);let s=Math.abs(a.bearing%360-(((m=t==null?void 0:t.wind)==null?void 0:m.degree)%360||0));s=s>180?360-s:s;const e=F.directionFactor(s,(p=t==null?void 0:t.wind)==null?void 0:p.scale),c=F.vesselTagFactor(a.displacement,a.loadCondition,a.tag,(l=t==null?void 0:t.wind)==null?void 0:l.scale);let r=e*n*c/100*a.speed;r=Math.round(r*1.943844*1e4)/1e4*-1;const f=F.waveHeightFactor(((b=(u=t==null?void 0:t.wave)==null?void 0:u.sig)==null?void 0:b.height)??1);return r=r*.24+f*.76,B.debug("weather factor = %s",r),Math.round(r*100)/100}static async analyseInstant(a,t,i,o,n,s="",e=0,c=!1,r={}){var L,C,V,X,tt;const f=y().valueOf();a.lng=x.LngLatHelper.convertToStdLng(a.lng);const{route:m,waypoints:p}=n.points,l=x.LaneHelper.calculateSubRoute(a,m);if(((L=l[0])==null?void 0:L.length)<=1)return;const{v0:u,label:b}=a.sog?{v0:a.sog,label:"Other"}:{v0:o.speed,label:"CP"},k=F.assembleProperties(i,o.loadCondition,u,0),d=p.length?x.LaneHelper.calculateSubWaypoints(a,p):[],g={from:{...a},route:l,waypoints:d,v0:u,label:b},h={hours:[],days:[],wps:[]};e||(x.LaneHelper.calculateRouteDistance(l)/o.speed<=72?e=3:e=6);let v=x.LaneHelper.simplifyRouteToCoordinates(l,d,0),I=0,M=0,E=0,q=0;t=y(t).utc();const A=t.clone();for(;v.length>0;){const P=e-t.hour()%e,$=Math.ceil(t.clone().add(P,"h").set({minute:0,second:0,millisecond:0}).diff(t,"h",!0)*1e4)/1e4,N=await F.speedLoseInHoursStep(k,t,A,$,I,v,s,c,r);(C=N.from)!=null&&C.speed&&(h.hours.push(N.from),h.wps.push(...N.wps),h.days.push(...N.days)),v=N==null?void 0:N.next,v.length||h.hours.push(N==null?void 0:N.to),I+=((V=N==null?void 0:N.to)==null?void 0:V.distanceFromPrevious)??0}const S=h.hours;for(let P=0;P<S.length-1;P++){const $=y(S[P+1].eta).diff(S[P].etd,"hour",!0)||1;M+=S[P].wxFactor*$,E+=S[P].cFactor*$,q+=$}(X=h.wps)==null||X.forEach((P,$)=>{if($){const N=h.wps[$-1],ct=P.distanceFromStart-N.distanceFromStart,et=y(P.eta).diff(y(N.etd),"h",!0);et<1?P.avgSpd=N.speed:P.avgSpd=Math.round(ct/et*100)/100}}),g.sample=h;const H=h.hours.at(-1);g.distance=Math.round(H.distanceFromStart*1e4)/1e4,g.eta=y(H.eta).toDate(),g.wxFactor=Math.round(M/q*1e4)/1e4,g.cFactor=Math.round(E/q*1e4)/1e4,g.avgSpeed=Math.round(H.distanceFromStart/q*1e4)/1e4,g.totalHrs=Math.round(q*1e4)/1e4,g.totalFoCons=Math.round((o==null?void 0:o.fo)/24*g.totalHrs*1e3)/1e3,g.totalDgoCons=Math.round((o==null?void 0:o.dgo)/24*g.totalHrs*1e3)/1e3;const W=y().valueOf()-f,T=((tt=h==null?void 0:h.hours)==null?void 0:tt.length)||1;return B.info("[%s] each hour-sample speed analyse cost: (%d / %d = %d) ms",r==null?void 0:r.requestId,W,T,Math.round(W/T*1e3)/1e3),g}static async analyseInstantWithThreshed(a,t,i,o,n,s,e="",c=3,r=!1,f={}){var E,q,A;a.lng=x.LngLatHelper.convertToStdLng(a.lng);const m=F.assembleProperties(o,n.loadCondition,n.speed,0),p=x.LaneHelper.calculateSubRoute(a,s);if(((E=p[0])==null?void 0:E.length)<=1)return;let l=x.LaneHelper.simplifyRouteToCoordinates(p,[],0),u=0,b=0,k=0,d=0,g;const h={hours:[],days:[]};for(t=y(t).utc();l.length>0;){const S=c-t.hour()%c;let H=Math.ceil(t.clone().add(S,"h").set({minute:0,second:0,millisecond:0}).diff(t,"h",!0)*1e4)/1e4;if(H=t.clone().add(H,"h").isAfter(i)?i.diff(t,"h",!0)*1e4/1e4:H,H)g=await F.speedLoseInHoursStep(m,t,i.clone(),H,u,l,e,r,f),(q=g.from)!=null&&q.speed&&h.hours.push(g.from),l=g==null?void 0:g.next,l.length||(h.hours.push(g==null?void 0:g.to),h.days.push(g.to)),u+=((A=g==null?void 0:g.to)==null?void 0:A.distanceFromPrevious)??0;else{g&&(h.hours.push(g.to),h.days.push(g.to));break}}const v=h.hours;for(let S=0;S<v.length-1;S++){const H=y(v[S+1].eta).diff(v[S].etd,"hour",!0);b+=v[S].wxFactor*H,k+=v[S].cFactor*H,d+=H}const I=h.hours.at(-1);return{sample:h,distance:Math.round(((I==null?void 0:I.distanceFromStart)||0)*1e4)/1e4,eta:y(I==null?void 0:I.eta).utc().format(),wxFactor:Math.round(b/d*1e4)/1e4,cFactor:Math.round(k/d*1e4)/1e4,avgSpeed:Math.round(((I==null?void 0:I.distanceFromStart)||0)/d*1e4)/1e4,totalHrs:Math.round(d*1e4)/1e4,to:I,route:x.LaneHelper.generateRouteAccordingToWaypoints(l)}}}j.AISImpl=Z,j.AlertHelper=G,j.AlertLevel=z,j.HifleetImpl=ot,j.LoadCondition=J,j.MyShipImpl=it,j.MyVesselImpl=at,j.ShipxyImpl=nt,j.SpeedHelper=F,j.SpeedLabel=Q,j.VesselTag=U,j.alertHelper=rt,Object.defineProperty(j,Symbol.toStringTag,{value:"Module"})});
@@ -0,0 +1,216 @@
1
+ import { Current, Meteo } from '@idm-plugin/meteo';
2
+ import moment from 'moment';
3
+ /**
4
+ * 船舶标签,不同标签适用不同分析模型
5
+ */
6
+ export declare enum VesselTag {
7
+ common = "common",
8
+ container = "container"
9
+ }
10
+ /**
11
+ * 船舶加载状态,laden/ ballast
12
+ */
13
+ export declare enum LoadCondition {
14
+ Ballast = "Ballast",
15
+ Laden = "Laden"
16
+ }
17
+ /**
18
+ * 速度标签
19
+ */
20
+ export declare enum SpeedLabel {
21
+ Cp = "CP",
22
+ Perf = "Basis",
23
+ Instruct = "Other"
24
+ }
25
+ /**
26
+ * 船舶基础档案
27
+ */
28
+ export interface VesselAssemble {
29
+ imo?: number;
30
+ lbp?: number;
31
+ length?: number;
32
+ lengthOverall?: number;
33
+ draught?: number;
34
+ breadthMoulded?: number;
35
+ breadth?: number;
36
+ breadthExtreme?: number;
37
+ type?: string;
38
+ deadweight: number;
39
+ }
40
+ /**
41
+ * 船舶运行参数,用于分析船舶失速、油耗、性能、碳排等
42
+ */
43
+ export interface VesselProperties {
44
+ tag: VesselTag;
45
+ loadCondition: LoadCondition | string;
46
+ draught: number;
47
+ displacement: number;
48
+ breadthMoulded: number;
49
+ lbp: number;
50
+ speed: number;
51
+ bearing: number;
52
+ }
53
+ export declare class SpeedHelper {
54
+ /**
55
+ * @see https://baike.baidu.com/item/%E6%96%B9%E5%BD%A2%E7%B3%BB%E6%95%B0/4965568?fr=aladdin
56
+ * 方形系数(block coefficient)
57
+ * 是指与基平面相平行的任一水线面以下的船型排水体积与对应的船长、型宽
58
+ * 和平均吃水的乘积所表示的长方体体积之比
59
+ * 方块系数介于 0.55 - 0.85
60
+ * @param displacement 排水 m^3
61
+ * @param length 水线长 m
62
+ * @param breadth 水线宽 m
63
+ * @param draught 吃水 m
64
+ * @return [0.55, 0.85]
65
+ */
66
+ private static blockCoefficient;
67
+ /**
68
+ * @see https://baike.baidu.com/item/%E5%BC%97%E5%8A%B3%E5%BE%B7%E6%95%B0/228891?fromModule=search-result_lemma-recommend
69
+ * 弗劳德数 (froude number)
70
+ * 流体力学中表征流体惯性力和重力相对大小的一个无量纲参数,记为Fr。它表示惯性力和重力量级的比,即:Fr=sqrt(U²/(gL)) [6] ,
71
+ * 式中U为物体运动速度,g为重力加速度;L为物体的特征长度
72
+ * 佛勞德數介乎 0.05 - 0.30
73
+ * @param speed 速度 m/s
74
+ * @param length 船长 m
75
+ * @param g 重力加速度 9.80 m/s^2
76
+ * @return [0.05, 0.30]
77
+ */
78
+ private static froudeNumber;
79
+ /**
80
+ * 失速修正系數
81
+ * @param bc 方形系数 [0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85]
82
+ * @param fr 弗劳德数 [0.05 - 0.30]
83
+ * @param loadCondition
84
+ * @private
85
+ */
86
+ private static amendFactor;
87
+ /**
88
+ * 失速方向因子
89
+ * 頂浪(不規則 波)、頂風 (0°): 2 * 𝐶𝜇 = 2
90
+ * 艏斜浪(不規 則波)、前迎風 (30°~60°): 2 𝐶𝜇 = 1.7 −0.03(Bn − 4)^2
91
+ * 橫浪(不規則 波)、橫風 (60°~150°): 2 𝐶𝜇 = 0.9 −0.06(Bn − 6)^2
92
+ * 順浪(不規則 波)、順風 (150°~180°): 2 𝐶𝜇 = 0.4 −0.03(Bn− 8)^2
93
+ *
94
+ * @param beta 航向与风浪的夹角
95
+ * @param bn
96
+ * @private
97
+ */
98
+ private static directionFactor;
99
+ /**
100
+ * 失速船型因子
101
+ * 集装箱(普通裝載狀態): 0.7Bn + Bn^6.5/(22 * ∇^(2/3))
102
+ * 散货等(满载): 0.5Bn + Bn^6.5 2 /(2.7 * ∇^(2/3))
103
+ * 散货等(压载): 0.7Bn + Bn^6.5 2 /(2.7 * ∇^(2/3))
104
+ * @param displacement 排水量,m^3
105
+ * @param loadCondition
106
+ * @param tag
107
+ * @param bn
108
+ * @private
109
+ */
110
+ private static vesselTagFactor;
111
+ /**
112
+ * 浪高影响因子
113
+ * @param ht 浪高,单位m
114
+ * @private
115
+ */
116
+ private static waveHeightFactor;
117
+ /**
118
+ * 组装船舶运行参数
119
+ * @param vessel 船舶基础档案
120
+ * @param loadCondition 装载状态
121
+ * @param speed 速度,kts
122
+ * @param bearing 方位角
123
+ * @private
124
+ */
125
+ private static assembleProperties;
126
+ /**
127
+ * 船舶特定时间的瞬时失速样本
128
+ * @param props 船舶属性
129
+ * @param coordinate {lat: 纬度, lng: 经度, velocity: 速度(kts, 可选,指定速度) }
130
+ * @param eta 位置时间
131
+ * @param source [CMEMS, GFS, Other]
132
+ * @param role 1: 船东, 2: 租家, 0: 未知
133
+ * @param useRouteParam true 启用设置速度
134
+ */
135
+ private static speedLoseAt;
136
+ /**
137
+ * 基于步长计算失速样本
138
+ * @param props 船舶属性(失速模型依赖)
139
+ * @param etd 出发时间
140
+ * @param etd4Day 前行过程中动态计算加24小时的天(etd + 24 * n)
141
+ * @param hours 前行的步长(小时)
142
+ * @param distanceFromStart 与最开始起点的距离
143
+ * @param keypoints 剩下的航路点
144
+ * @param source 气象数据源: CMEMS / GFS
145
+ * @param useRouteParam true 启用航线上设置的参数 { suspend: 停留时长(小时), velocity: 速度(kts)}
146
+ * @private
147
+ */
148
+ private static speedLoseInHoursStep;
149
+ /**
150
+ * 洋流影响因子
151
+ * @param bearing 船舶航行方位角
152
+ * @param current 洋流要素
153
+ * @param role 1: 船东, 2: 租家, 0: 未知
154
+ */
155
+ static currentFactor(bearing: number, current: Current, role?: number): number;
156
+ /**
157
+ * 风浪影响因子
158
+ * @param props 船舶档案
159
+ * @param wwc 气象要素
160
+ */
161
+ static weatherFactor(props: VesselProperties, wwc: Meteo): number;
162
+ /**
163
+ * 全程失速分析(走完航程)
164
+ * @param from 起点 {lng, lat}
165
+ * @param etd 出发时间,YYYY-MM-DDTHH:mm:ssZ
166
+ * @param vessel 船舶属性 @see VesselAssemble, 注意吃水为船舶实时吃水,非档案中的固定吃水,一般可以从ais获取; 如获取不得,用档案吃水代替
167
+ * @param cp { loadCondition, speed } LoadCondition: Ballast or Laden, speed(kts)
168
+ * @param lane 航线 { points: { route, waypoints }}
169
+ * @param source 气象数据源,GFS or CMEMES, 默认CMEMS
170
+ * @param stepHrs 样本步长, 0表示动态计算(6 or 3 hrs)
171
+ * @param useRouteParam
172
+ */
173
+ static analyseInstant(from: any, etd: any, vessel: VesselAssemble, cp: {
174
+ loadCondition: string;
175
+ speed: number;
176
+ fo: number;
177
+ dgo: number;
178
+ }, lane: {
179
+ points: {
180
+ route: number[][][];
181
+ waypoints: any[];
182
+ };
183
+ voyage: any;
184
+ }, source?: string, stepHrs?: number, useRouteParam?: boolean, options?: {
185
+ requestId?: string;
186
+ }): Promise<any>;
187
+ /**
188
+ * 分段失速分析(最多走hours 小时)
189
+ * @param from 起点 {lng, lat}
190
+ * @param etd 出发时间,YYYY-MM-DDTHH:mm:ssZ
191
+ * @param threshed 单次所走上限时间
192
+ * @param vessel 船舶属性 @see VesselAssemble, 注意吃水为船舶实时吃水,非档案中的固定吃水,一般可以从ais获取; 如获取不得,用档案吃水代替
193
+ * @param cp { loadCondition, speed } LoadCondition: Ballast or Laden, speed(kts)
194
+ * @param route 航路[[[lng, lat]]]
195
+ * @param source 气象数据源,GFS or CMEMES, 默认CMEMS
196
+ * @param stepHrs
197
+ */
198
+ static analyseInstantWithThreshed(from: any, etd: any, threshed: moment.Moment, vessel: VesselAssemble, cp: {
199
+ loadCondition: string;
200
+ speed: number;
201
+ fo: number;
202
+ dgo: number;
203
+ }, route: number[][][], source?: string, stepHrs?: number, useRouteParam?: boolean, options?: {
204
+ requestId?: string;
205
+ }): Promise<{
206
+ sample: any;
207
+ distance: number;
208
+ eta: string;
209
+ wxFactor: number;
210
+ cFactor: number;
211
+ avgSpeed: number;
212
+ totalHrs: number;
213
+ to: any;
214
+ route: any;
215
+ } | undefined>;
216
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@idm-plugin/vessel",
3
3
  "private": false,
4
- "version": "1.1.8",
4
+ "version": "1.2.0",
5
5
  "description": "idm plugin for vessel",
6
6
  "type": "module",
7
7
  "keywords": [
@@ -27,6 +27,8 @@
27
27
  "release": "yarn build && yarn publish --access public"
28
28
  },
29
29
  "dependencies": {
30
+ "@idm-plugin/geo": "^1.2.7",
31
+ "@idm-plugin/meteo": "^0.0.2",
30
32
  "@log4js-node/log4js-api": "^1.0.2",
31
33
  "got": "11",
32
34
  "moment": "^2.30.1"